Files
FlClash/lib/state.dart
T
chen08209 5984c161c5 fix(core): keep very large profiles from crashing the app
Changelog: Large profiles no longer crash; downloads trust core CAs
2026-10-16 22:54:53 +08:00

140 lines
3.7 KiB
Dart

import 'dart:async';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/ui/ui.dart';
import 'package:flutter/foundation.dart';
import 'package:material_ui/material_ui.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'common/common.dart';
import 'enum/enum.dart';
import 'models/models.dart';
class GlobalState {
static GlobalState? _instance;
GlobalKey<NavigatorState> get navigatorKey => rootNavigatorKey;
late String appEnv;
late PackageInfo packageInfo;
late Measure measure;
late ProviderContainer container;
bool needInitStatus = true;
bool get isPre => appEnv != 'stable';
bool get canCrashCore => canCrashCoreFor(isDebug: kDebugMode, appEnv: appEnv);
@visibleForTesting
static bool canCrashCoreFor({required bool isDebug, required String appEnv}) {
return isDebug || appEnv == 'dev';
}
String? lastConfigMd5;
ConfigCounts? lastConfigCounts;
bool isAttach = false;
GlobalState._internal();
factory GlobalState() {
_instance ??= GlobalState._internal();
return _instance!;
}
String get ua => container
.read(patchClashConfigProvider.select((state) => state.globalUa))
.takeFirstValid([packageInfo.ua]);
Future<T?> loadingRun<T>(
FutureOr<T> Function() futureFunction, {
String? title,
required LoadingTag? tag,
bool silence = false,
}) async {
return globalState.safeRun(
futureFunction,
silence: silence,
title: title,
onStart: () {
if (tag != null) {
container.read(loadingProvider(tag).notifier).start();
}
},
onEnd: () {
if (tag != null) {
container.read(loadingProvider(tag).notifier).stop();
}
},
);
}
/// Concurrent [loadingRun]s would let the first to finish clear [tag].
Future<void> batchRun<T>(
List<T> items,
Future<void> Function(T item) task, {
required String Function(T item) label,
required int concurrency,
required LoadingTag? tag,
}) async {
final pool = TaskPool(concurrency);
final failures = List<UpdatingMessage?>.filled(items.length, null);
await loadingRun(
() => Future.wait([
for (final (index, item) in items.indexed)
pool.run(() async {
try {
await task(item);
} catch (e, s) {
commonPrint.log(
'${label(item)} ===> ${compactError(e)}, $s',
logLevel: LogLevel.warning,
);
failures[index] = UpdatingMessage(
label: label(item),
message: userFacingErrorMessage(e, currentAppLocalizations),
);
}
}),
]),
tag: tag,
);
dialogs.showFailures(failures.nonNulls.toList());
}
Future<T?> safeRun<T>(
FutureOr<T> Function() futureFunction, {
String? title,
VoidCallback? onStart,
VoidCallback? onEnd,
bool silence = true,
}) async {
try {
onStart?.call();
return await futureFunction();
} catch (e, s) {
commonPrint.log(
title == null
? '${compactError(e)}, $s'
: '$title ===> ${compactError(e)}, $s',
logLevel: LogLevel.warning,
);
final message = userFacingErrorMessage(e, currentAppLocalizations);
if (silence) {
dialogs.showNotifier(message, level: MessageLevel.error);
} else {
unawaited(
dialogs.showMessage(
title: title ?? currentAppLocalizations.tip,
message: TextSpan(text: message),
),
);
}
return null;
} finally {
onEnd?.call();
}
}
}
final globalState = GlobalState();