5984c161c5
Changelog: Large profiles no longer crash; downloads trust core CAs
140 lines
3.7 KiB
Dart
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();
|