diff --git a/assets/backgrounds/.gitkeep b/assets/backgrounds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/backgrounds/SC_01_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_01_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..5e315b3 Binary files /dev/null and b/assets/backgrounds/SC_01_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_02_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_02_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..77150ab Binary files /dev/null and b/assets/backgrounds/SC_02_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_03_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_03_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..43267f7 Binary files /dev/null and b/assets/backgrounds/SC_03_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_04_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_04_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..8d0b3b0 Binary files /dev/null and b/assets/backgrounds/SC_04_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_05_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_05_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..a5d753f Binary files /dev/null and b/assets/backgrounds/SC_05_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_06_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_06_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..7803a62 Binary files /dev/null and b/assets/backgrounds/SC_06_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_07_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_07_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..eb50423 Binary files /dev/null and b/assets/backgrounds/SC_07_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_08_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_08_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..f6a1514 Binary files /dev/null and b/assets/backgrounds/SC_08_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_09_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_09_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..22f273c Binary files /dev/null and b/assets/backgrounds/SC_09_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_10_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_10_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..cad3c1b Binary files /dev/null and b/assets/backgrounds/SC_10_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_11_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_11_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..569bfc4 Binary files /dev/null and b/assets/backgrounds/SC_11_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_12_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_12_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..fa8b598 Binary files /dev/null and b/assets/backgrounds/SC_12_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_13_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_13_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..0c0b7b4 Binary files /dev/null and b/assets/backgrounds/SC_13_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_14_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_14_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..72da7b3 Binary files /dev/null and b/assets/backgrounds/SC_14_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_15_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_15_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..2915b70 Binary files /dev/null and b/assets/backgrounds/SC_15_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_16_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_16_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..c6eb567 Binary files /dev/null and b/assets/backgrounds/SC_16_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_17_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_17_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..40da70b Binary files /dev/null and b/assets/backgrounds/SC_17_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_18_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_18_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..a6203a0 Binary files /dev/null and b/assets/backgrounds/SC_18_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_19_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_19_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..cd884bd Binary files /dev/null and b/assets/backgrounds/SC_19_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_20_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_20_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..0653552 Binary files /dev/null and b/assets/backgrounds/SC_20_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_21_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_21_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..7a1f23f Binary files /dev/null and b/assets/backgrounds/SC_21_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_22_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_22_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..12b8a46 Binary files /dev/null and b/assets/backgrounds/SC_22_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_23_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_23_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..b789fd2 Binary files /dev/null and b/assets/backgrounds/SC_23_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_24_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_24_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..c91ba0e Binary files /dev/null and b/assets/backgrounds/SC_24_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_25_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_25_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..c67e601 Binary files /dev/null and b/assets/backgrounds/SC_25_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_26_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_26_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..d5e1ac9 Binary files /dev/null and b/assets/backgrounds/SC_26_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_27_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_27_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..ddf1963 Binary files /dev/null and b/assets/backgrounds/SC_27_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_28_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_28_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..a1b1adc Binary files /dev/null and b/assets/backgrounds/SC_28_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_29_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_29_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..085a5be Binary files /dev/null and b/assets/backgrounds/SC_29_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_30_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_30_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..777bbf3 Binary files /dev/null and b/assets/backgrounds/SC_30_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_31_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_31_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..4957f30 Binary files /dev/null and b/assets/backgrounds/SC_31_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_32_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_32_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..5166c1c Binary files /dev/null and b/assets/backgrounds/SC_32_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_33_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_33_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..1b4c49b Binary files /dev/null and b/assets/backgrounds/SC_33_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_34_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_34_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..e17e760 Binary files /dev/null and b/assets/backgrounds/SC_34_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_35_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_35_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..b3f583e Binary files /dev/null and b/assets/backgrounds/SC_35_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_36_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_36_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..38dc682 Binary files /dev/null and b/assets/backgrounds/SC_36_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_37_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_37_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..ef83ced Binary files /dev/null and b/assets/backgrounds/SC_37_Wallpaper_3840x2160.webp differ diff --git a/assets/backgrounds/SC_38_Wallpaper_3840x2160.webp b/assets/backgrounds/SC_38_Wallpaper_3840x2160.webp new file mode 100644 index 0000000..d9e39c2 Binary files /dev/null and b/assets/backgrounds/SC_38_Wallpaper_3840x2160.webp differ diff --git a/lib/app.dart b/lib/app.dart index e10b723..b945658 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,7 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'package:fluent_ui/fluent_ui.dart'; -import 'package:flutter_acrylic/flutter_acrylic.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:go_router/go_router.dart'; import 'package:hexcolor/hexcolor.dart'; @@ -14,18 +15,15 @@ import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/ui/guide/guide_ui.dart'; import 'package:starcitizen_doctor/ui/home/performance/performance_ui.dart'; import 'package:starcitizen_doctor/ui/splash_ui.dart'; -import 'package:device_info_plus/device_info_plus.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; import 'package:uuid/uuid.dart'; -import 'package:window_manager/window_manager.dart'; import 'api/analytics.dart'; import 'api/api.dart'; import 'common/conf/url_conf.dart'; import 'common/helper/system_helper.dart'; import 'common/io/rs_http.dart'; -import 'common/rust/frb_generated.dart'; -import 'common/rust/api/win32_api.dart' as win32; +// import 'common/rust/api/win32_api.dart' as win32; // Web 不支持 import 'data/app_version_data.dart'; import 'generated/no_l10n_strings.dart'; import 'ui/home/downloader/home_downloader_ui.dart'; @@ -49,24 +47,24 @@ abstract class AppGlobalState with _$AppGlobalState { @Default(ThemeConf()) ThemeConf themeConf, Locale? appLocale, Box? appConfBox, + @Default("assets/backgrounds/SC_01_Wallpaper_3840x2160.webp") String backgroundImageAssetsPath, }) = _AppGlobalState; } @riverpod GoRouter router(Ref ref) { return GoRouter( + initialLocation: '/splash', routes: [ + GoRoute(path: '/splash', pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI())), GoRoute( path: '/', - pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI()), - ), - GoRoute( - path: '/index', pageBuilder: (context, state) => myPageBuilder(context, state, const IndexUI()), routes: [ GoRoute( - path: "downloader", - pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI())), + path: "downloader", + pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI()), + ), GoRoute( path: 'game_doctor', pageBuilder: (context, state) => myPageBuilder(context, state, const HomeGameDoctorUI()), @@ -76,17 +74,19 @@ GoRouter router(Ref ref) { pageBuilder: (context, state) => myPageBuilder(context, state, const HomePerformanceUI()), ), GoRoute( - path: 'advanced_localization', - pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI())) + path: 'advanced_localization', + pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI()), + ), ], ), - GoRoute(path: '/tools', builder: (_, _) => const SizedBox(), routes: [ - GoRoute( - path: 'unp4kc', - pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI()), - ), - ]), - GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())) + GoRoute( + path: '/tools', + builder: (_, _) => const SizedBox(), + routes: [ + GoRoute(path: 'unp4kc', pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI())), + ], + ), + GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())), ], ); } @@ -94,13 +94,13 @@ GoRouter router(Ref ref) { @riverpod class AppGlobalModel extends _$AppGlobalModel { static Map get appLocaleSupport => { - const Locale("auto"): S.current.settings_app_language_auto, - const Locale("zh", "CN"): NoL10n.langZHS, - const Locale("zh", "TW"): NoL10n.langZHT, - const Locale("en"): NoL10n.langEn, - const Locale("ja"): NoL10n.langJa, - const Locale("ru"): NoL10n.langRU, - }; + const Locale("auto"): S.current.settings_app_language_auto, + const Locale("zh", "CN"): NoL10n.langZHS, + const Locale("zh", "TW"): NoL10n.langZHT, + const Locale("en"): NoL10n.langEn, + const Locale("ja"): NoL10n.langJa, + const Locale("ru"): NoL10n.langRU, + }; @override AppGlobalState build() { @@ -113,15 +113,12 @@ class AppGlobalModel extends _$AppGlobalModel { if (_initialized) return; // init Data final applicationSupportDir = await _initAppDir(); - - // init Rust bridge - await RustLib.init(); await RSHttp.init(); dPrint("---- rust bridge init -----"); // init Hive try { - Hive.init("$applicationSupportDir/db"); + if (!kIsWeb) Hive.init("$applicationSupportDir/db"); final box = await Hive.openBox("app_conf"); state = state.copyWith(appConfBox: box); if (box.get("install_id", defaultValue: "") == "") { @@ -141,13 +138,16 @@ class AppGlobalModel extends _$AppGlobalModel { } state = state.copyWith(deviceUUID: deviceUUID, appLocale: locale); } catch (e) { - await win32.setForegroundWindow(windowName: "SCToolBox"); + // Web 平台不支持 win32 API + if (!kIsWeb) { + // await win32.setForegroundWindow(windowName: "SCToolBox"); + } dPrint("exit: db is locking ..."); - exit(0); + if (!kIsWeb) exit(0); } // init powershell - if (Platform.isWindows) { + if (!kIsWeb && Platform.isWindows) { try { await SystemHelper.initPowershellPath(); dPrint("---- Powershell init -----"); @@ -157,35 +157,63 @@ class AppGlobalModel extends _$AppGlobalModel { } // get windows info - WindowsDeviceInfo? windowsDeviceInfo; - try { - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - windowsDeviceInfo = await deviceInfo.windowsInfo; - } catch (e) { - dPrint("DeviceInfo.windowsInfo error: $e"); - } + // WindowsDeviceInfo? windowsDeviceInfo; + // try { + // DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + // windowsDeviceInfo = await deviceInfo.windowsInfo; + // } catch (e) { + // dPrint("DeviceInfo.windowsInfo error: $e"); + // } // init windows - windowManager.waitUntilReadyToShow().then((_) async { - await windowManager.setTitle("SCToolBox"); - await windowManager.setSkipTaskbar(false); - await windowManager.show(); - if (Platform.isWindows) { - await Window.initialize(); - await Window.hideWindowControls(); - if (windowsDeviceInfo?.productName.contains("Windows 11") ?? false) { - await Window.setEffect( - effect: WindowEffect.acrylic, - ); - } - } - }); + // if (!kIsWeb) { + // windowManager.waitUntilReadyToShow().then((_) async { + // await windowManager.setTitle("SCToolBox"); + // await windowManager.setSkipTaskbar(false); + // await windowManager.show(); + // if (Platform.isWindows) { + // await Window.initialize(); + // await Window.hideWindowControls(); + // if (windowsDeviceInfo?.productName.contains("Windows 11") ?? false) { + // await Window.setEffect(effect: WindowEffect.acrylic); + // } + // } + // }); + // } dPrint("---- Window init -----"); + if (kIsWeb) { + _startBackgroundLoop(); + } _initialized = true; ref.keepAlive(); } + Timer? _loopTimer; + + void _startBackgroundLoop() async { + _loopTimer?.cancel(); + _loopTimer = null; + final assetManifest = await AssetManifest.loadFromAssetBundle(rootBundle); + final imageAssetsList = assetManifest + .listAssets() + .where((string) => string.startsWith("assets/backgrounds")) + .toList(); + + void rollImage() { + final random = DateTime.now().millisecondsSinceEpoch % imageAssetsList.length; + final image = imageAssetsList[random]; + state = state.copyWith(backgroundImageAssetsPath: image); + dPrint("rollImage: [$random] $image"); + } + + rollImage(); + // 使用 timer 每 30 秒 更换一次随机图片 + _loopTimer = Timer.periodic(const Duration(seconds: 30), (timer) { + rollImage(); + }); + } + String getUpgradePath() { return "${state.applicationSupportDir}/._upgrade"; } @@ -194,7 +222,7 @@ class AppGlobalModel extends _$AppGlobalModel { // ignore: avoid_build_context_in_providers Future checkUpdate(BuildContext context) async { - if (!ConstConf.isMSE) { + if (!kIsWeb && !ConstConf.isMSE) { final dir = Directory(getUpgradePath()); if (await dir.exists()) { dir.delete(recursive: true); @@ -226,18 +254,25 @@ class AppGlobalModel extends _$AppGlobalModel { if (state.networkVersionData == null) { if (!context.mounted) return false; await showToast( - context, S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString())); + context, + S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString()), + ); return false; } + if (kIsWeb) return false; // Web 版本不支持自动更新 if (!Platform.isWindows) return false; - final lastVersion = - ConstConf.isMSE ? state.networkVersionData?.mSELastVersionCode : state.networkVersionData?.lastVersionCode; + final lastVersion = ConstConf.isMSE + ? state.networkVersionData?.mSELastVersionCode + : state.networkVersionData?.lastVersionCode; if ((lastVersion ?? 0) > ConstConf.appVersionCode) { // need update if (!context.mounted) return false; - final r = - await showDialog(dismissWithEsc: false, context: context, builder: (context) => const UpgradeDialogUI()); + final r = await showDialog( + dismissWithEsc: false, + context: context, + builder: (context) => const UpgradeDialogUI(), + ); if (r != true) { if (!context.mounted) return false; @@ -264,8 +299,10 @@ class AppGlobalModel extends _$AppGlobalModel { dPrint("now == $now start == $startTime end == $endTime"); if (now < startTime) { - _activityThemeColorTimer = - Timer(Duration(milliseconds: startTime - now), () => checkActivityThemeColor(networkVersionData)); + _activityThemeColorTimer = Timer( + Duration(milliseconds: startTime - now), + () => checkActivityThemeColor(networkVersionData), + ); dPrint("start Timer ...."); } else if (now >= startTime && now <= endTime) { dPrint("update Color ...."); @@ -280,8 +317,10 @@ class AppGlobalModel extends _$AppGlobalModel { ); // wait for end - _activityThemeColorTimer = - Timer(Duration(milliseconds: endTime - now), () => checkActivityThemeColor(networkVersionData)); + _activityThemeColorTimer = Timer( + Duration(milliseconds: endTime - now), + () => checkActivityThemeColor(networkVersionData), + ); } else { dPrint("reset Color ...."); state = state.copyWith( @@ -302,8 +341,9 @@ class AppGlobalModel extends _$AppGlobalModel { await appConfBox.put("app_locale", null); return; } - final localeCode = - value.countryCode != null ? "${value.languageCode}_${value.countryCode ?? ""}" : value.languageCode; + final localeCode = value.countryCode != null + ? "${value.languageCode}_${value.countryCode ?? ""}" + : value.languageCode; dPrint("changeLocale == $value localeCode=== $localeCode"); await appConfBox.put("app_locale", localeCode); state = state.copyWith(appLocale: value); @@ -311,6 +351,12 @@ class AppGlobalModel extends _$AppGlobalModel { } Future _initAppDir() async { + if (kIsWeb) { + await Future.delayed(const Duration(milliseconds: 10)); + // Web 版本不需要本地目录 + state = state.copyWith(applicationSupportDir: "", applicationBinaryModuleDir: ""); + return Future.value(""); + } if (Platform.isWindows) { final userProfileDir = Platform.environment["USERPROFILE"]; final applicationSupportDir = (await getApplicationSupportDirectory()).absolute.path; diff --git a/lib/app.freezed.dart b/lib/app.freezed.dart index 65c7d9a..347c544 100644 --- a/lib/app.freezed.dart +++ b/lib/app.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$AppGlobalState { - String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; + String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; String get backgroundImageAssetsPath; /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $AppGlobalStateCopyWith get copyWith => _$AppGlobalStateCopyWith @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&(identical(other.backgroundImageAssetsPath, backgroundImageAssetsPath) || other.backgroundImageAssetsPath == backgroundImageAssetsPath)); } @override -int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox); +int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,backgroundImageAssetsPath); @override String toString() { - return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)'; + return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, backgroundImageAssetsPath: $backgroundImageAssetsPath)'; } @@ -45,7 +45,7 @@ abstract mixin class $AppGlobalStateCopyWith<$Res> { factory $AppGlobalStateCopyWith(AppGlobalState value, $Res Function(AppGlobalState) _then) = _$AppGlobalStateCopyWithImpl; @useResult $Res call({ - String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox + String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, String backgroundImageAssetsPath }); @@ -62,7 +62,7 @@ class _$AppGlobalStateCopyWithImpl<$Res> /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? backgroundImageAssetsPath = null,}) { return _then(_self.copyWith( deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable @@ -71,7 +71,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable -as Box?, +as Box?,backgroundImageAssetsPath: null == backgroundImageAssetsPath ? _self.backgroundImageAssetsPath : backgroundImageAssetsPath // ignore: cast_nullable_to_non_nullable +as String, )); } /// Create a copy of AppGlobalState @@ -165,10 +166,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, String backgroundImageAssetsPath)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _AppGlobalState() when $default != null: -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.backgroundImageAssetsPath);case _: return orElse(); } @@ -186,10 +187,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, String backgroundImageAssetsPath) $default,) {final _that = this; switch (_that) { case _AppGlobalState(): -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.backgroundImageAssetsPath);case _: throw StateError('Unexpected subclass'); } @@ -206,10 +207,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, String backgroundImageAssetsPath)? $default,) {final _that = this; switch (_that) { case _AppGlobalState() when $default != null: -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.backgroundImageAssetsPath);case _: return null; } @@ -221,7 +222,7 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi class _AppGlobalState implements AppGlobalState { - const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox}); + const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox, this.backgroundImageAssetsPath = "assets/backgrounds/SC_01_Wallpaper_3840x2160.webp"}); @override final String? deviceUUID; @@ -231,6 +232,7 @@ class _AppGlobalState implements AppGlobalState { @override@JsonKey() final ThemeConf themeConf; @override final Locale? appLocale; @override final Box? appConfBox; +@override@JsonKey() final String backgroundImageAssetsPath; /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. @@ -242,16 +244,16 @@ _$AppGlobalStateCopyWith<_AppGlobalState> get copyWith => __$AppGlobalStateCopyW @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&(identical(other.backgroundImageAssetsPath, backgroundImageAssetsPath) || other.backgroundImageAssetsPath == backgroundImageAssetsPath)); } @override -int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox); +int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,backgroundImageAssetsPath); @override String toString() { - return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)'; + return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, backgroundImageAssetsPath: $backgroundImageAssetsPath)'; } @@ -262,7 +264,7 @@ abstract mixin class _$AppGlobalStateCopyWith<$Res> implements $AppGlobalStateCo factory _$AppGlobalStateCopyWith(_AppGlobalState value, $Res Function(_AppGlobalState) _then) = __$AppGlobalStateCopyWithImpl; @override @useResult $Res call({ - String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox + String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, String backgroundImageAssetsPath }); @@ -279,7 +281,7 @@ class __$AppGlobalStateCopyWithImpl<$Res> /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? backgroundImageAssetsPath = null,}) { return _then(_AppGlobalState( deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable @@ -288,7 +290,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable -as Box?, +as Box?,backgroundImageAssetsPath: null == backgroundImageAssetsPath ? _self.backgroundImageAssetsPath : backgroundImageAssetsPath // ignore: cast_nullable_to_non_nullable +as String, )); } diff --git a/lib/app.g.dart b/lib/app.g.dart index ad8194c..b09f961 100644 --- a/lib/app.g.dart +++ b/lib/app.g.dart @@ -48,7 +48,7 @@ final class RouterProvider } } -String _$routerHash() => r'62dd494daf9b176547e30da83b1923ccdea13b4f'; +String _$routerHash() => r'eb81af4202a3a92cf95447ecfb6a698f9a8cd122'; @ProviderFor(AppGlobalModel) const appGlobalModelProvider = AppGlobalModelProvider._(); @@ -82,7 +82,7 @@ final class AppGlobalModelProvider } } -String _$appGlobalModelHash() => r'53dd9ed5e197333b509d282eb01073f15572820c'; +String _$appGlobalModelHash() => r'8bd5efc6fb95ad9d06875e6fb18bf46ae222fafa'; abstract class _$AppGlobalModel extends $Notifier { AppGlobalState build(); diff --git a/lib/common/conf/url_conf.dart b/lib/common/conf/url_conf.dart index cee49ce..c3e4969 100644 --- a/lib/common/conf/url_conf.dart +++ b/lib/common/conf/url_conf.dart @@ -6,9 +6,9 @@ import 'package:starcitizen_doctor/common/utils/log.dart'; class URLConf { /// HOME API - static String gitApiHome = "https://git.scbox.xkeyc.cn"; - static String newsApiHome = "https://scbox.citizenwiki.cn"; - static const String analyticsApiHome = "https://scbox.org"; + static String gitApiHome = "https://ecdn.git.scbox.xkeyc.cn"; + static String newsApiHome = "https://ecdn.news.scbox.xkeyc.cn"; + static const String analyticsApiHome = "https://web-proxy.scbox.xkeyc.cn/analytics/analytics"; static bool isUrlCheckPass = false; @@ -28,7 +28,7 @@ class URLConf { static const feedbackUrl = "https://support.citizenwiki.cn/all"; static const feedbackFAQUrl = "https://support.citizenwiki.cn/t/sc-toolbox"; static String nav42KitUrl = - "https://payload.citizenwiki.cn/api/community-navs?sort=is_sponsored&depth=2&page=1&limit=1000"; + "https://ecdn.42nav.xkeyc.cn/api/community-navs?sort=is_sponsored&depth=2&page=1&limit=1000"; static String get devReleaseUrl => "$gitApiHome/SCToolBox/Release/releases"; diff --git a/lib/common/helper/log_helper.dart b/lib/common/helper/log_helper.dart index 115693d..eb428f4 100644 --- a/lib/common/helper/log_helper.dart +++ b/lib/common/helper/log_helper.dart @@ -1,13 +1,14 @@ import 'dart:convert'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:hive_ce/hive.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; class SCLoggerHelper { static Future getLogFilePath() async { - if (!Platform.isWindows) return null; + if (kIsWeb || !Platform.isWindows) return null; Map envVars = Platform.environment; final appDataPath = envVars["appdata"]; if (appDataPath == null) { @@ -31,7 +32,7 @@ class SCLoggerHelper { } static Future getLauncherLogList() async { - if (!Platform.isWindows) return []; + if (kIsWeb || !Platform.isWindows) return []; try { final jsonLogPath = await getLogFilePath(); if (jsonLogPath == null) throw "no file path"; @@ -43,9 +44,11 @@ class SCLoggerHelper { } } - static Future> getGameInstallPath(List listData, - {bool checkExists = true, - List withVersion = const ["LIVE"]}) async { + static Future> getGameInstallPath( + List listData, { + bool checkExists = true, + List withVersion = const ["LIVE"], + }) async { List scInstallPaths = []; checkAndAddPath(String path, bool checkExists) async { @@ -55,8 +58,7 @@ class SCLoggerHelper { if (!checkExists) { dPrint("find installPath == $path"); scInstallPaths.add(path); - } else if (await File("$path/Bin64/StarCitizen.exe").exists() && - await File("$path/Data.p4k").exists()) { + } else if (await File("$path/Bin64/StarCitizen.exe").exists() && await File("$path/Data.p4k").exists()) { dPrint("find installPath == $path"); scInstallPaths.add(path); } @@ -73,8 +75,7 @@ class SCLoggerHelper { try { for (var v in withVersion) { - String pattern = - r'([a-zA-Z]:\\\\[^\\\\]*\\\\[^\\\\]*\\\\StarCitizen\\\\' + v + r')'; + String pattern = r'([a-zA-Z]:\\\\[^\\\\]*\\\\[^\\\\]*\\\\StarCitizen\\\\' + v + r')'; RegExp regExp = RegExp(pattern, caseSensitive: false); for (var i = listData.length - 1; i > 0; i--) { final line = listData[i]; @@ -91,8 +92,7 @@ class SCLoggerHelper { for (var v in withVersion) { if (fileName.toString().endsWith(v)) { for (var nv in withVersion) { - final nextName = - "${fileName.toString().replaceAll("\\$v", "")}\\$nv"; + final nextName = "${fileName.toString().replaceAll("\\$v", "")}\\$nv"; await checkAndAddPath(nextName, true); } } @@ -121,8 +121,7 @@ class SCLoggerHelper { if (!await logFile.exists()) { return null; } - return await logFile.readAsLines( - encoding: const Utf8Codec(allowMalformed: true)); + return await logFile.readAsLines(encoding: const Utf8Codec(allowMalformed: true)); } static MapEntry? getGameRunningLogInfo(List logs) { @@ -138,47 +137,47 @@ class SCLoggerHelper { static MapEntry? _checkRunningLine(String line) { if (line.contains("STATUS_CRYENGINE_OUT_OF_SYSMEM")) { - return MapEntry(S.current.doctor_game_error_low_memory, - S.current.doctor_game_error_low_memory_info); + return MapEntry(S.current.doctor_game_error_low_memory, S.current.doctor_game_error_low_memory_info); } if (line.contains("EXCEPTION_ACCESS_VIOLATION")) { - return MapEntry(S.current.doctor_game_error_generic_info, - "https://docs.qq.com/doc/DUURxUVhzTmZoY09Z"); + return MapEntry(S.current.doctor_game_error_generic_info, "https://docs.qq.com/doc/DUURxUVhzTmZoY09Z"); } if (line.contains("DXGI_ERROR_DEVICE_REMOVED")) { - return MapEntry(S.current.doctor_game_error_gpu_crash, - "https://www.bilibili.com/read/cv19335199"); + return MapEntry(S.current.doctor_game_error_gpu_crash, "https://www.bilibili.com/read/cv19335199"); } if (line.contains("Wakeup socket sendto error")) { - return MapEntry(S.current.doctor_game_error_socket_error, - S.current.doctor_game_error_socket_error_info); + return MapEntry(S.current.doctor_game_error_socket_error, S.current.doctor_game_error_socket_error_info); } if (line.contains("The requested operation requires elevated")) { - return MapEntry(S.current.doctor_game_error_permissions_error, - S.current.doctor_game_error_permissions_error_info); + return MapEntry( + S.current.doctor_game_error_permissions_error, + S.current.doctor_game_error_permissions_error_info, + ); } - if (line.contains( - "The process cannot access the file because is is being used by another process")) { - return MapEntry(S.current.doctor_game_error_game_process_error, - S.current.doctor_game_error_game_process_error_info); + if (line.contains("The process cannot access the file because is is being used by another process")) { + return MapEntry( + S.current.doctor_game_error_game_process_error, + S.current.doctor_game_error_game_process_error_info, + ); } if (line.contains("0xc0000043")) { - return MapEntry(S.current.doctor_game_error_game_damaged_file, - S.current.doctor_game_error_game_damaged_file_info); + return MapEntry( + S.current.doctor_game_error_game_damaged_file, + S.current.doctor_game_error_game_damaged_file_info, + ); } if (line.contains("option to verify the content of the Data.p4k file")) { - return MapEntry(S.current.doctor_game_error_game_damaged_p4k_file, - S.current.doctor_game_error_game_damaged_p4k_file_info); + return MapEntry( + S.current.doctor_game_error_game_damaged_p4k_file, + S.current.doctor_game_error_game_damaged_p4k_file_info, + ); } if (line.contains("OUTOFMEMORY Direct3D could not allocate")) { - return MapEntry(S.current.doctor_game_error_low_gpu_memory, - S.current.doctor_game_error_low_gpu_memory_info); + return MapEntry(S.current.doctor_game_error_low_gpu_memory, S.current.doctor_game_error_low_gpu_memory_info); } - if (line.contains( - "try disabling with r_vulkanDisableLayers = 1 in your user.cfg")) { - return MapEntry(S.current.doctor_game_error_gpu_vulkan_crash, - S.current.doctor_game_error_gpu_vulkan_crash_info); + if (line.contains("try disabling with r_vulkanDisableLayers = 1 in your user.cfg")) { + return MapEntry(S.current.doctor_game_error_gpu_vulkan_crash, S.current.doctor_game_error_gpu_vulkan_crash_info); } /// Unknown diff --git a/lib/common/helper/system_helper.dart b/lib/common/helper/system_helper.dart index a1e9be6..05252b8 100644 --- a/lib/common/helper/system_helper.dart +++ b/lib/common/helper/system_helper.dart @@ -30,7 +30,7 @@ class SystemHelper { "-Path", "\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"", "-Name", - "\"ForcedPhysicalSectorSizeInBytes\"" + "\"ForcedPhysicalSectorSizeInBytes\"", ]); dPrint("checkNvmePatchStatus result ==== ${result.stdout}"); if (result.stderr == "" && result.stdout.toString().contains("{* 4095}")) { @@ -52,7 +52,7 @@ class SystemHelper { "ForcedPhysicalSectorSizeInBytes", "-PropertyType MultiString", "-Force -Value", - "\"* 4095\"" + "\"* 4095\"", ]); dPrint("nvme_PhysicalBytes result == ${result.stdout}"); return result.stderr; @@ -65,7 +65,7 @@ class SystemHelper { "-Path", "\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"", "-Name", - "\"ForcedPhysicalSectorSizeInBytes\"" + "\"ForcedPhysicalSectorSizeInBytes\"", ]); dPrint("doRemoveNvmePath result ==== ${result.stdout}"); if (result.stderr == "") { @@ -97,8 +97,9 @@ class SystemHelper { "$programDataPath\\Microsoft\\Windows\\Start Menu\\Programs\\Roberts Space Industries\\RSI Launcher.lnk"; final rsiLinkFile = File(rsiFilePath); if (await rsiLinkFile.exists()) { - final r = await Process.run(SystemHelper.powershellPath, - ["(New-Object -ComObject WScript.Shell).CreateShortcut(\"$rsiFilePath\").targetpath"]); + final r = await Process.run(SystemHelper.powershellPath, [ + "(New-Object -ComObject WScript.Shell).CreateShortcut(\"$rsiFilePath\").targetpath", + ]); if (r.stdout.toString().contains("RSI Launcher.exe")) { final start = r.stdout.toString().split("RSI Launcher.exe"); if (skipEXE) { @@ -147,22 +148,15 @@ class SystemHelper { if (processorAffinity == null) { Process.run(path, []); } else { - Process.run("cmd.exe", [ - '/C', - 'Start', - '""', - '/High', - '/Affinity', - processorAffinity, - path, - ]); + Process.run("cmd.exe", ['/C', 'Start', '""', '/High', '/Affinity', processorAffinity, path]); } dPrint(path); } static Future getSystemMemorySizeGB() async { - final r = await Process.run( - powershellPath, ["(Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb"]); + final r = await Process.run(powershellPath, [ + "(Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb", + ]); return int.tryParse(r.stdout.toString().trim()) ?? 0; } @@ -196,10 +190,9 @@ foreach ($adapter in $adapterMemory) { } static Future getDiskInfo() async { - return (await Process.run(powershellPath, ["Get-PhysicalDisk | format-table BusType,FriendlyName,Size"])) - .stdout - .toString() - .trim(); + return (await Process.run(powershellPath, [ + "Get-PhysicalDisk | format-table BusType,FriendlyName,Size", + ])).stdout.toString().trim(); } static Future getDirLen(String path, {List? skipPath}) async { @@ -226,8 +219,9 @@ foreach ($adapter in $adapterMemory) { } static Future getNumberOfLogicalProcessors() async { - final cpuNumberResult = - await Process.run(powershellPath, ["(Get-WmiObject -Class Win32_Processor).NumberOfLogicalProcessors"]); + final cpuNumberResult = await Process.run(powershellPath, [ + "(Get-WmiObject -Class Win32_Processor).NumberOfLogicalProcessors", + ]); if (cpuNumberResult.exitCode != 0) return 0; return int.tryParse(cpuNumberResult.stdout.toString().trim()) ?? 0; } @@ -257,8 +251,10 @@ foreach ($adapter in $adapterMemory) { static Future openDir(dynamic path, {bool isFile = false}) async { dPrint("SystemHelper.openDir path === $path"); if (Platform.isWindows) { - await Process.run( - SystemHelper.powershellPath, ["explorer.exe", isFile ? "/select,$path" : "\"/select,\"$path\"\""]); + await Process.run(SystemHelper.powershellPath, [ + "explorer.exe", + isFile ? "/select,$path" : "\"/select,\"$path\"\"", + ]); } } diff --git a/lib/common/io/rs_http.dart b/lib/common/io/rs_http.dart index ac0956f..8f2870d 100644 --- a/lib/common/io/rs_http.dart +++ b/lib/common/io/rs_http.dart @@ -1,17 +1,27 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:dio/dio.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; -import 'package:starcitizen_doctor/common/rust/api/http_api.dart' as rust_http; -import 'package:starcitizen_doctor/common/rust/api/http_api.dart'; import 'package:starcitizen_doctor/common/rust/http_package.dart'; class RSHttp { + static late Dio _dio; + static Map _defaultHeaders = {}; + static Future init() async { - await rust_http.setDefaultHeader(headers: { + _defaultHeaders = { "User-Agent": - "SCToolBox/${ConstConf.appVersion} (${ConstConf.appVersionCode})${ConstConf.isMSE ? "" : " DEV"} RSHttp" - }); + "SCToolBox/${ConstConf.appVersion} (${ConstConf.appVersionCode})${ConstConf.isMSE ? "" : " DEV"} RSHttp", + }; + + _dio = Dio( + BaseOptions( + headers: _defaultHeaders, + responseType: ResponseType.bytes, + validateStatus: (status) => true, // 接受所有状态码 + ), + ); } static Future get( @@ -20,14 +30,13 @@ class RSHttp { String? withIpAddress, bool? withCustomDns, }) async { - final r = await rust_http.fetch( - method: MyMethod.gets, + return await _fetch( + method: 'GET', url: url, headers: headers, withIpAddress: withIpAddress, withCustomDns: withCustomDns, ); - return r; } static Future getText( @@ -36,10 +45,7 @@ class RSHttp { String? withIpAddress, bool? withCustomDns, }) async { - final r = await get(url, - headers: headers, - withIpAddress: withIpAddress, - withCustomDns: withCustomDns); + final r = await get(url, headers: headers, withIpAddress: withIpAddress, withCustomDns: withCustomDns); if (r.data == null) return ""; final str = utf8.decode(r.data!); return str; @@ -57,15 +63,14 @@ class RSHttp { headers ??= {}; headers["Content-Type"] = contentType; } - final r = await rust_http.fetch( - method: MyMethod.post, + return await _fetch( + method: 'POST', url: url, headers: headers, - inputData: data, + data: data, withIpAddress: withIpAddress, withCustomDns: withCustomDns, ); - return r; } static Future head( @@ -74,21 +79,81 @@ class RSHttp { String? withIpAddress, bool? withCustomDns, }) async { - final r = await rust_http.fetch( - method: MyMethod.head, + return await _fetch( + method: 'HEAD', url: url, headers: headers, withIpAddress: withIpAddress, withCustomDns: withCustomDns, ); - return r; } static Future> dnsLookupTxt(String host) async { - return await rust_http.dnsLookupTxt(host: host); + // TXT 记录查询在 Web 平台上无法实现,返回空列表 + return []; } static Future> dnsLookupIps(String host) async { - return await rust_http.dnsLookupIps(host: host); + // Web 平台无法直接查询 DNS,返回空列表 + // 在 Web 平台上,DNS 解析由浏览器自动处理 + return []; + } + + static Future _fetch({ + required String method, + required String url, + Map? headers, + Uint8List? data, + String? withIpAddress, + bool? withCustomDns, + }) async { + try { + final mergedHeaders = {..._defaultHeaders, ...?headers}; + + final response = await _dio.request( + url, + data: data, + options: Options( + method: method, + headers: mergedHeaders, + responseType: ResponseType.bytes, + validateStatus: (status) => true, + ), + ); + + // 将 Dio Response 转换为 RustHttpResponse + return RustHttpResponse( + statusCode: response.statusCode ?? 0, + headers: _convertHeaders(response.headers.map), + url: response.realUri.toString(), + contentLength: response.headers.value('content-length') != null + ? BigInt.from(int.parse(response.headers.value('content-length')!)) + : null, + version: MyHttpVersion.httpUnknown, + remoteAddr: response.realUri.host, + data: response.data is Uint8List ? response.data : null, + ); + } catch (e) { + // 发生错误时返回默认响应 + return RustHttpResponse( + statusCode: 0, + headers: {}, + url: url, + contentLength: null, + version: MyHttpVersion.httpUnknown, + remoteAddr: '', + data: null, + ); + } + } + + static Map _convertHeaders(Map> headers) { + final result = {}; + headers.forEach((key, value) { + if (value.isNotEmpty) { + result[key] = value.join(', '); + } + }); + return result; } } diff --git a/lib/common/rust/api/asar_api.dart b/lib/common/rust/api/asar_api.dart deleted file mode 100644 index 44eb4e3..0000000 --- a/lib/common/rust/api/asar_api.dart +++ /dev/null @@ -1,44 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import - -import '../frb_generated.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; - -Future getRsiLauncherAsarData({ - required String asarPath, -}) => RustLib.instance.api.crateApiAsarApiGetRsiLauncherAsarData( - asarPath: asarPath, -); - -class RsiLauncherAsarData { - final String asarPath; - final String mainJsPath; - final Uint8List mainJsContent; - - const RsiLauncherAsarData({ - required this.asarPath, - required this.mainJsPath, - required this.mainJsContent, - }); - - Future writeMainJs({required List content}) => - RustLib.instance.api.crateApiAsarApiRsiLauncherAsarDataWriteMainJs( - that: this, - content: content, - ); - - @override - int get hashCode => - asarPath.hashCode ^ mainJsPath.hashCode ^ mainJsContent.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RsiLauncherAsarData && - runtimeType == other.runtimeType && - asarPath == other.asarPath && - mainJsPath == other.mainJsPath && - mainJsContent == other.mainJsContent; -} diff --git a/lib/common/rust/api/http_api.dart b/lib/common/rust/api/http_api.dart deleted file mode 100644 index 15c3f36..0000000 --- a/lib/common/rust/api/http_api.dart +++ /dev/null @@ -1,37 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import - -import '../frb_generated.dart'; -import '../http_package.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; - -// These functions are ignored because they are not marked as `pub`: `_my_method_to_hyper_method` - -Future setDefaultHeader({required Map headers}) => - RustLib.instance.api.crateApiHttpApiSetDefaultHeader(headers: headers); - -Future fetch({ - required MyMethod method, - required String url, - Map? headers, - Uint8List? inputData, - String? withIpAddress, - bool? withCustomDns, -}) => RustLib.instance.api.crateApiHttpApiFetch( - method: method, - url: url, - headers: headers, - inputData: inputData, - withIpAddress: withIpAddress, - withCustomDns: withCustomDns, -); - -Future> dnsLookupTxt({required String host}) => - RustLib.instance.api.crateApiHttpApiDnsLookupTxt(host: host); - -Future> dnsLookupIps({required String host}) => - RustLib.instance.api.crateApiHttpApiDnsLookupIps(host: host); - -enum MyMethod { options, gets, post, put, delete, head, trace, connect, patch } diff --git a/lib/common/rust/api/rs_process.dart b/lib/common/rust/api/rs_process.dart deleted file mode 100644 index 0a7d334..0000000 --- a/lib/common/rust/api/rs_process.dart +++ /dev/null @@ -1,50 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import - -import '../frb_generated.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; - -// These functions are ignored because they are not marked as `pub`: `_process_output` -// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `RsProcess` -// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone` - -Stream start({ - required String executable, - required List arguments, - required String workingDirectory, -}) => RustLib.instance.api.crateApiRsProcessStart( - executable: executable, - arguments: arguments, - workingDirectory: workingDirectory, -); - -Future write({required int rsPid, required String data}) => - RustLib.instance.api.crateApiRsProcessWrite(rsPid: rsPid, data: data); - -class RsProcessStreamData { - final RsProcessStreamDataType dataType; - final String data; - final int rsPid; - - const RsProcessStreamData({ - required this.dataType, - required this.data, - required this.rsPid, - }); - - @override - int get hashCode => dataType.hashCode ^ data.hashCode ^ rsPid.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RsProcessStreamData && - runtimeType == other.runtimeType && - dataType == other.dataType && - data == other.data && - rsPid == other.rsPid; -} - -enum RsProcessStreamDataType { output, error, exit } diff --git a/lib/common/rust/api/win32_api.dart b/lib/common/rust/api/win32_api.dart index e0f4ccd..cfe1229 100644 --- a/lib/common/rust/api/win32_api.dart +++ b/lib/common/rust/api/win32_api.dart @@ -1,24 +1,15 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. +// Web platform stub for win32_api +// Windows API 在 Web 平台上不可用,提供空实现 -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import +/// 发送系统通知(Web 平台使用控制台输出) +Future sendNotify({String? summary, String? body, String? appName, String? appId}) async { + print('Notification: $summary - $body'); + // Web 平台可以使用浏览器 Notification API + // 但需要用户授权,这里简化处理 +} -import '../frb_generated.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; - -Future sendNotify({ - String? summary, - String? body, - String? appName, - String? appId, -}) => RustLib.instance.api.crateApiWin32ApiSendNotify( - summary: summary, - body: body, - appName: appName, - appId: appId, -); - -Future setForegroundWindow({required String windowName}) => RustLib - .instance - .api - .crateApiWin32ApiSetForegroundWindow(windowName: windowName); +/// 设置窗口为前台(Web 平台不支持) +Future setForegroundWindow({required String windowName}) async { + print('setForegroundWindow not supported on web platform'); + return false; +} diff --git a/lib/common/rust/frb_generated.dart b/lib/common/rust/frb_generated.dart deleted file mode 100644 index f7d0ba7..0000000 --- a/lib/common/rust/frb_generated.dart +++ /dev/null @@ -1,1291 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field - -import 'api/asar_api.dart'; -import 'api/http_api.dart'; -import 'api/rs_process.dart'; -import 'api/win32_api.dart'; -import 'dart:async'; -import 'dart:convert'; -import 'frb_generated.dart'; -import 'frb_generated.io.dart' - if (dart.library.js_interop) 'frb_generated.web.dart'; -import 'http_package.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; - -/// Main entrypoint of the Rust API -class RustLib extends BaseEntrypoint { - @internal - static final instance = RustLib._(); - - RustLib._(); - - /// Initialize flutter_rust_bridge - static Future init({ - RustLibApi? api, - BaseHandler? handler, - ExternalLibrary? externalLibrary, - bool forceSameCodegenVersion = true, - }) async { - await instance.initImpl( - api: api, - handler: handler, - externalLibrary: externalLibrary, - forceSameCodegenVersion: forceSameCodegenVersion, - ); - } - - /// Initialize flutter_rust_bridge in mock mode. - /// No libraries for FFI are loaded. - static void initMock({required RustLibApi api}) { - instance.initMockImpl(api: api); - } - - /// Dispose flutter_rust_bridge - /// - /// The call to this function is optional, since flutter_rust_bridge (and everything else) - /// is automatically disposed when the app stops. - static void dispose() => instance.disposeImpl(); - - @override - ApiImplConstructor get apiImplConstructor => - RustLibApiImpl.new; - - @override - WireConstructor get wireConstructor => - RustLibWire.fromExternalLibrary; - - @override - Future executeRustInitializers() async {} - - @override - ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => - kDefaultExternalLibraryLoaderConfig; - - @override - String get codegenVersion => '2.11.1'; - - @override - int get rustContentHash => 1832496273; - - static const kDefaultExternalLibraryLoaderConfig = - ExternalLibraryLoaderConfig( - stem: 'rust', - ioDirectory: 'rust/target/release/', - webPrefix: 'pkg/', - ); -} - -abstract class RustLibApi extends BaseApi { - Future> crateApiHttpApiDnsLookupIps({required String host}); - - Future> crateApiHttpApiDnsLookupTxt({required String host}); - - Future crateApiHttpApiFetch({ - required MyMethod method, - required String url, - Map? headers, - Uint8List? inputData, - String? withIpAddress, - bool? withCustomDns, - }); - - Future crateApiAsarApiGetRsiLauncherAsarData({ - required String asarPath, - }); - - Future crateApiAsarApiRsiLauncherAsarDataWriteMainJs({ - required RsiLauncherAsarData that, - required List content, - }); - - Future crateApiWin32ApiSendNotify({ - String? summary, - String? body, - String? appName, - String? appId, - }); - - Future crateApiHttpApiSetDefaultHeader({ - required Map headers, - }); - - Future crateApiWin32ApiSetForegroundWindow({ - required String windowName, - }); - - Stream crateApiRsProcessStart({ - required String executable, - required List arguments, - required String workingDirectory, - }); - - Future crateApiRsProcessWrite({ - required int rsPid, - required String data, - }); -} - -class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { - RustLibApiImpl({ - required super.handler, - required super.wire, - required super.generalizedFrbRustBinding, - required super.portManager, - }); - - @override - Future> crateApiHttpApiDnsLookupIps({required String host}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_String(host); - return wire.wire__crate__api__http_api__dns_lookup_ips(port_, arg0); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_list_String, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiHttpApiDnsLookupIpsConstMeta, - argValues: [host], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiHttpApiDnsLookupIpsConstMeta => - const TaskConstMeta(debugName: "dns_lookup_ips", argNames: ["host"]); - - @override - Future> crateApiHttpApiDnsLookupTxt({required String host}) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_String(host); - return wire.wire__crate__api__http_api__dns_lookup_txt(port_, arg0); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_list_String, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiHttpApiDnsLookupTxtConstMeta, - argValues: [host], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiHttpApiDnsLookupTxtConstMeta => - const TaskConstMeta(debugName: "dns_lookup_txt", argNames: ["host"]); - - @override - Future crateApiHttpApiFetch({ - required MyMethod method, - required String url, - Map? headers, - Uint8List? inputData, - String? withIpAddress, - bool? withCustomDns, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_my_method(method); - var arg1 = cst_encode_String(url); - var arg2 = cst_encode_opt_Map_String_String_None(headers); - var arg3 = cst_encode_opt_list_prim_u_8_strict(inputData); - var arg4 = cst_encode_opt_String(withIpAddress); - var arg5 = cst_encode_opt_box_autoadd_bool(withCustomDns); - return wire.wire__crate__api__http_api__fetch( - port_, - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_rust_http_response, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiHttpApiFetchConstMeta, - argValues: [ - method, - url, - headers, - inputData, - withIpAddress, - withCustomDns, - ], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiHttpApiFetchConstMeta => const TaskConstMeta( - debugName: "fetch", - argNames: [ - "method", - "url", - "headers", - "inputData", - "withIpAddress", - "withCustomDns", - ], - ); - - @override - Future crateApiAsarApiGetRsiLauncherAsarData({ - required String asarPath, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_String(asarPath); - return wire.wire__crate__api__asar_api__get_rsi_launcher_asar_data( - port_, - arg0, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_rsi_launcher_asar_data, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiAsarApiGetRsiLauncherAsarDataConstMeta, - argValues: [asarPath], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiAsarApiGetRsiLauncherAsarDataConstMeta => - const TaskConstMeta( - debugName: "get_rsi_launcher_asar_data", - argNames: ["asarPath"], - ); - - @override - Future crateApiAsarApiRsiLauncherAsarDataWriteMainJs({ - required RsiLauncherAsarData that, - required List content, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_box_autoadd_rsi_launcher_asar_data(that); - var arg1 = cst_encode_list_prim_u_8_loose(content); - return wire - .wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js( - port_, - arg0, - arg1, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_unit, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiAsarApiRsiLauncherAsarDataWriteMainJsConstMeta, - argValues: [that, content], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiAsarApiRsiLauncherAsarDataWriteMainJsConstMeta => - const TaskConstMeta( - debugName: "rsi_launcher_asar_data_write_main_js", - argNames: ["that", "content"], - ); - - @override - Future crateApiWin32ApiSendNotify({ - String? summary, - String? body, - String? appName, - String? appId, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_opt_String(summary); - var arg1 = cst_encode_opt_String(body); - var arg2 = cst_encode_opt_String(appName); - var arg3 = cst_encode_opt_String(appId); - return wire.wire__crate__api__win32_api__send_notify( - port_, - arg0, - arg1, - arg2, - arg3, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_unit, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiWin32ApiSendNotifyConstMeta, - argValues: [summary, body, appName, appId], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiWin32ApiSendNotifyConstMeta => const TaskConstMeta( - debugName: "send_notify", - argNames: ["summary", "body", "appName", "appId"], - ); - - @override - Future crateApiHttpApiSetDefaultHeader({ - required Map headers, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_Map_String_String_None(headers); - return wire.wire__crate__api__http_api__set_default_header( - port_, - arg0, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiHttpApiSetDefaultHeaderConstMeta, - argValues: [headers], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiHttpApiSetDefaultHeaderConstMeta => - const TaskConstMeta( - debugName: "set_default_header", - argNames: ["headers"], - ); - - @override - Future crateApiWin32ApiSetForegroundWindow({ - required String windowName, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_String(windowName); - return wire.wire__crate__api__win32_api__set_foreground_window( - port_, - arg0, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_bool, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kCrateApiWin32ApiSetForegroundWindowConstMeta, - argValues: [windowName], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiWin32ApiSetForegroundWindowConstMeta => - const TaskConstMeta( - debugName: "set_foreground_window", - argNames: ["windowName"], - ); - - @override - Stream crateApiRsProcessStart({ - required String executable, - required List arguments, - required String workingDirectory, - }) { - final streamSink = RustStreamSink(); - unawaited( - handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_String(executable); - var arg1 = cst_encode_list_String(arguments); - var arg2 = cst_encode_String(workingDirectory); - var arg3 = cst_encode_StreamSink_rs_process_stream_data_Dco( - streamSink, - ); - return wire.wire__crate__api__rs_process__start( - port_, - arg0, - arg1, - arg2, - arg3, - ); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiRsProcessStartConstMeta, - argValues: [executable, arguments, workingDirectory, streamSink], - apiImpl: this, - ), - ), - ); - return streamSink.stream; - } - - TaskConstMeta get kCrateApiRsProcessStartConstMeta => const TaskConstMeta( - debugName: "start", - argNames: ["executable", "arguments", "workingDirectory", "streamSink"], - ); - - @override - Future crateApiRsProcessWrite({ - required int rsPid, - required String data, - }) { - return handler.executeNormal( - NormalTask( - callFfi: (port_) { - var arg0 = cst_encode_u_32(rsPid); - var arg1 = cst_encode_String(data); - return wire.wire__crate__api__rs_process__write(port_, arg0, arg1); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_unit, - decodeErrorData: null, - ), - constMeta: kCrateApiRsProcessWriteConstMeta, - argValues: [rsPid, data], - apiImpl: this, - ), - ); - } - - TaskConstMeta get kCrateApiRsProcessWriteConstMeta => - const TaskConstMeta(debugName: "write", argNames: ["rsPid", "data"]); - - @protected - AnyhowException dco_decode_AnyhowException(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return AnyhowException(raw as String); - } - - @protected - Map dco_decode_Map_String_String_None(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return Map.fromEntries( - dco_decode_list_record_string_string( - raw, - ).map((e) => MapEntry(e.$1, e.$2)), - ); - } - - @protected - RustStreamSink - dco_decode_StreamSink_rs_process_stream_data_Dco(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - throw UnimplementedError(); - } - - @protected - String dco_decode_String(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as String; - } - - @protected - bool dco_decode_bool(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as bool; - } - - @protected - bool dco_decode_box_autoadd_bool(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as bool; - } - - @protected - RsiLauncherAsarData dco_decode_box_autoadd_rsi_launcher_asar_data( - dynamic raw, - ) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return dco_decode_rsi_launcher_asar_data(raw); - } - - @protected - BigInt dco_decode_box_autoadd_u_64(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return dco_decode_u_64(raw); - } - - @protected - int dco_decode_i_32(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as int; - } - - @protected - List dco_decode_list_String(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return (raw as List).map(dco_decode_String).toList(); - } - - @protected - List dco_decode_list_prim_u_8_loose(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as List; - } - - @protected - Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as Uint8List; - } - - @protected - List<(String, String)> dco_decode_list_record_string_string(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return (raw as List).map(dco_decode_record_string_string).toList(); - } - - @protected - MyHttpVersion dco_decode_my_http_version(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return MyHttpVersion.values[raw as int]; - } - - @protected - MyMethod dco_decode_my_method(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return MyMethod.values[raw as int]; - } - - @protected - Map? dco_decode_opt_Map_String_String_None(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw == null ? null : dco_decode_Map_String_String_None(raw); - } - - @protected - String? dco_decode_opt_String(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw == null ? null : dco_decode_String(raw); - } - - @protected - bool? dco_decode_opt_box_autoadd_bool(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw == null ? null : dco_decode_box_autoadd_bool(raw); - } - - @protected - BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw == null ? null : dco_decode_box_autoadd_u_64(raw); - } - - @protected - Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw == null ? null : dco_decode_list_prim_u_8_strict(raw); - } - - @protected - (String, String) dco_decode_record_string_string(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - final arr = raw as List; - if (arr.length != 2) { - throw Exception('Expected 2 elements, got ${arr.length}'); - } - return (dco_decode_String(arr[0]), dco_decode_String(arr[1])); - } - - @protected - RsProcessStreamData dco_decode_rs_process_stream_data(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); - return RsProcessStreamData( - dataType: dco_decode_rs_process_stream_data_type(arr[0]), - data: dco_decode_String(arr[1]), - rsPid: dco_decode_u_32(arr[2]), - ); - } - - @protected - RsProcessStreamDataType dco_decode_rs_process_stream_data_type(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return RsProcessStreamDataType.values[raw as int]; - } - - @protected - RsiLauncherAsarData dco_decode_rsi_launcher_asar_data(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); - return RsiLauncherAsarData( - asarPath: dco_decode_String(arr[0]), - mainJsPath: dco_decode_String(arr[1]), - mainJsContent: dco_decode_list_prim_u_8_strict(arr[2]), - ); - } - - @protected - RustHttpResponse dco_decode_rust_http_response(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - final arr = raw as List; - if (arr.length != 7) - throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); - return RustHttpResponse( - statusCode: dco_decode_u_16(arr[0]), - headers: dco_decode_Map_String_String_None(arr[1]), - url: dco_decode_String(arr[2]), - contentLength: dco_decode_opt_box_autoadd_u_64(arr[3]), - version: dco_decode_my_http_version(arr[4]), - remoteAddr: dco_decode_String(arr[5]), - data: dco_decode_opt_list_prim_u_8_strict(arr[6]), - ); - } - - @protected - int dco_decode_u_16(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as int; - } - - @protected - int dco_decode_u_32(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as int; - } - - @protected - BigInt dco_decode_u_64(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return dcoDecodeU64(raw); - } - - @protected - int dco_decode_u_8(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return raw as int; - } - - @protected - void dco_decode_unit(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return; - } - - @protected - AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_String(deserializer); - return AnyhowException(inner); - } - - @protected - Map sse_decode_Map_String_String_None( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_list_record_string_string(deserializer); - return Map.fromEntries(inner.map((e) => MapEntry(e.$1, e.$2))); - } - - @protected - RustStreamSink - sse_decode_StreamSink_rs_process_stream_data_Dco( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - throw UnimplementedError('Unreachable ()'); - } - - @protected - String sse_decode_String(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_list_prim_u_8_strict(deserializer); - return utf8.decoder.convert(inner); - } - - @protected - bool sse_decode_bool(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint8() != 0; - } - - @protected - bool sse_decode_box_autoadd_bool(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return (sse_decode_bool(deserializer)); - } - - @protected - RsiLauncherAsarData sse_decode_box_autoadd_rsi_launcher_asar_data( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - return (sse_decode_rsi_launcher_asar_data(deserializer)); - } - - @protected - BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return (sse_decode_u_64(deserializer)); - } - - @protected - int sse_decode_i_32(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getInt32(); - } - - @protected - List sse_decode_list_String(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - var len_ = sse_decode_i_32(deserializer); - var ans_ = []; - for (var idx_ = 0; idx_ < len_; ++idx_) { - ans_.add(sse_decode_String(deserializer)); - } - return ans_; - } - - @protected - List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var len_ = sse_decode_i_32(deserializer); - return deserializer.buffer.getUint8List(len_); - } - - @protected - Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var len_ = sse_decode_i_32(deserializer); - return deserializer.buffer.getUint8List(len_); - } - - @protected - List<(String, String)> sse_decode_list_record_string_string( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - - var len_ = sse_decode_i_32(deserializer); - var ans_ = <(String, String)>[]; - for (var idx_ = 0; idx_ < len_; ++idx_) { - ans_.add(sse_decode_record_string_string(deserializer)); - } - return ans_; - } - - @protected - MyHttpVersion sse_decode_my_http_version(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_i_32(deserializer); - return MyHttpVersion.values[inner]; - } - - @protected - MyMethod sse_decode_my_method(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_i_32(deserializer); - return MyMethod.values[inner]; - } - - @protected - Map? sse_decode_opt_Map_String_String_None( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - - if (sse_decode_bool(deserializer)) { - return (sse_decode_Map_String_String_None(deserializer)); - } else { - return null; - } - } - - @protected - String? sse_decode_opt_String(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - if (sse_decode_bool(deserializer)) { - return (sse_decode_String(deserializer)); - } else { - return null; - } - } - - @protected - bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - if (sse_decode_bool(deserializer)) { - return (sse_decode_box_autoadd_bool(deserializer)); - } else { - return null; - } - } - - @protected - BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - if (sse_decode_bool(deserializer)) { - return (sse_decode_box_autoadd_u_64(deserializer)); - } else { - return null; - } - } - - @protected - Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - if (sse_decode_bool(deserializer)) { - return (sse_decode_list_prim_u_8_strict(deserializer)); - } else { - return null; - } - } - - @protected - (String, String) sse_decode_record_string_string( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - var var_field0 = sse_decode_String(deserializer); - var var_field1 = sse_decode_String(deserializer); - return (var_field0, var_field1); - } - - @protected - RsProcessStreamData sse_decode_rs_process_stream_data( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - var var_dataType = sse_decode_rs_process_stream_data_type(deserializer); - var var_data = sse_decode_String(deserializer); - var var_rsPid = sse_decode_u_32(deserializer); - return RsProcessStreamData( - dataType: var_dataType, - data: var_data, - rsPid: var_rsPid, - ); - } - - @protected - RsProcessStreamDataType sse_decode_rs_process_stream_data_type( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_i_32(deserializer); - return RsProcessStreamDataType.values[inner]; - } - - @protected - RsiLauncherAsarData sse_decode_rsi_launcher_asar_data( - SseDeserializer deserializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - var var_asarPath = sse_decode_String(deserializer); - var var_mainJsPath = sse_decode_String(deserializer); - var var_mainJsContent = sse_decode_list_prim_u_8_strict(deserializer); - return RsiLauncherAsarData( - asarPath: var_asarPath, - mainJsPath: var_mainJsPath, - mainJsContent: var_mainJsContent, - ); - } - - @protected - RustHttpResponse sse_decode_rust_http_response(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var var_statusCode = sse_decode_u_16(deserializer); - var var_headers = sse_decode_Map_String_String_None(deserializer); - var var_url = sse_decode_String(deserializer); - var var_contentLength = sse_decode_opt_box_autoadd_u_64(deserializer); - var var_version = sse_decode_my_http_version(deserializer); - var var_remoteAddr = sse_decode_String(deserializer); - var var_data = sse_decode_opt_list_prim_u_8_strict(deserializer); - return RustHttpResponse( - statusCode: var_statusCode, - headers: var_headers, - url: var_url, - contentLength: var_contentLength, - version: var_version, - remoteAddr: var_remoteAddr, - data: var_data, - ); - } - - @protected - int sse_decode_u_16(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint16(); - } - - @protected - int sse_decode_u_32(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint32(); - } - - @protected - BigInt sse_decode_u_64(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getBigUint64(); - } - - @protected - int sse_decode_u_8(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint8(); - } - - @protected - void sse_decode_unit(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - } - - @protected - bool cst_encode_bool(bool raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - int cst_encode_i_32(int raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - int cst_encode_my_http_version(MyHttpVersion raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_i_32(raw.index); - } - - @protected - int cst_encode_my_method(MyMethod raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_i_32(raw.index); - } - - @protected - int cst_encode_rs_process_stream_data_type(RsProcessStreamDataType raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_i_32(raw.index); - } - - @protected - int cst_encode_u_16(int raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - int cst_encode_u_32(int raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - int cst_encode_u_8(int raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - void cst_encode_unit(void raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - - @protected - void sse_encode_AnyhowException( - AnyhowException self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.message, serializer); - } - - @protected - void sse_encode_Map_String_String_None( - Map self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_list_record_string_string( - self.entries.map((e) => (e.key, e.value)).toList(), - serializer, - ); - } - - @protected - void sse_encode_StreamSink_rs_process_stream_data_Dco( - RustStreamSink self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String( - self.setupAndSerialize( - codec: DcoCodec( - decodeSuccessData: dco_decode_rs_process_stream_data, - decodeErrorData: dco_decode_AnyhowException, - ), - ), - serializer, - ); - } - - @protected - void sse_encode_String(String self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); - } - - @protected - void sse_encode_bool(bool self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint8(self ? 1 : 0); - } - - @protected - void sse_encode_box_autoadd_bool(bool self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_bool(self, serializer); - } - - @protected - void sse_encode_box_autoadd_rsi_launcher_asar_data( - RsiLauncherAsarData self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_rsi_launcher_asar_data(self, serializer); - } - - @protected - void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_u_64(self, serializer); - } - - @protected - void sse_encode_i_32(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putInt32(self); - } - - @protected - void sse_encode_list_String(List self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.length, serializer); - for (final item in self) { - sse_encode_String(item, serializer); - } - } - - @protected - void sse_encode_list_prim_u_8_loose( - List self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List( - self is Uint8List ? self : Uint8List.fromList(self), - ); - } - - @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List(self); - } - - @protected - void sse_encode_list_record_string_string( - List<(String, String)> self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.length, serializer); - for (final item in self) { - sse_encode_record_string_string(item, serializer); - } - } - - @protected - void sse_encode_my_http_version( - MyHttpVersion self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.index, serializer); - } - - @protected - void sse_encode_my_method(MyMethod self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.index, serializer); - } - - @protected - void sse_encode_opt_Map_String_String_None( - Map? self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - - sse_encode_bool(self != null, serializer); - if (self != null) { - sse_encode_Map_String_String_None(self, serializer); - } - } - - @protected - void sse_encode_opt_String(String? self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - sse_encode_bool(self != null, serializer); - if (self != null) { - sse_encode_String(self, serializer); - } - } - - @protected - void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - sse_encode_bool(self != null, serializer); - if (self != null) { - sse_encode_box_autoadd_bool(self, serializer); - } - } - - @protected - void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - - sse_encode_bool(self != null, serializer); - if (self != null) { - sse_encode_box_autoadd_u_64(self, serializer); - } - } - - @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - - sse_encode_bool(self != null, serializer); - if (self != null) { - sse_encode_list_prim_u_8_strict(self, serializer); - } - } - - @protected - void sse_encode_record_string_string( - (String, String) self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.$1, serializer); - sse_encode_String(self.$2, serializer); - } - - @protected - void sse_encode_rs_process_stream_data( - RsProcessStreamData self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_rs_process_stream_data_type(self.dataType, serializer); - sse_encode_String(self.data, serializer); - sse_encode_u_32(self.rsPid, serializer); - } - - @protected - void sse_encode_rs_process_stream_data_type( - RsProcessStreamDataType self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_i_32(self.index, serializer); - } - - @protected - void sse_encode_rsi_launcher_asar_data( - RsiLauncherAsarData self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.asarPath, serializer); - sse_encode_String(self.mainJsPath, serializer); - sse_encode_list_prim_u_8_strict(self.mainJsContent, serializer); - } - - @protected - void sse_encode_rust_http_response( - RustHttpResponse self, - SseSerializer serializer, - ) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_u_16(self.statusCode, serializer); - sse_encode_Map_String_String_None(self.headers, serializer); - sse_encode_String(self.url, serializer); - sse_encode_opt_box_autoadd_u_64(self.contentLength, serializer); - sse_encode_my_http_version(self.version, serializer); - sse_encode_String(self.remoteAddr, serializer); - sse_encode_opt_list_prim_u_8_strict(self.data, serializer); - } - - @protected - void sse_encode_u_16(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint16(self); - } - - @protected - void sse_encode_u_32(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint32(self); - } - - @protected - void sse_encode_u_64(BigInt self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putBigUint64(self); - } - - @protected - void sse_encode_u_8(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint8(self); - } - - @protected - void sse_encode_unit(void self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - } -} diff --git a/lib/common/rust/frb_generated.io.dart b/lib/common/rust/frb_generated.io.dart deleted file mode 100644 index e9a0a38..0000000 --- a/lib/common/rust/frb_generated.io.dart +++ /dev/null @@ -1,1113 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field - -import 'api/asar_api.dart'; -import 'api/http_api.dart'; -import 'api/rs_process.dart'; -import 'api/win32_api.dart'; -import 'dart:async'; -import 'dart:convert'; -import 'dart:ffi' as ffi; -import 'frb_generated.dart'; -import 'http_package.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; - -abstract class RustLibApiImplPlatform extends BaseApiImpl { - RustLibApiImplPlatform({ - required super.handler, - required super.wire, - required super.generalizedFrbRustBinding, - required super.portManager, - }); - - @protected - AnyhowException dco_decode_AnyhowException(dynamic raw); - - @protected - Map dco_decode_Map_String_String_None(dynamic raw); - - @protected - RustStreamSink - dco_decode_StreamSink_rs_process_stream_data_Dco(dynamic raw); - - @protected - String dco_decode_String(dynamic raw); - - @protected - bool dco_decode_bool(dynamic raw); - - @protected - bool dco_decode_box_autoadd_bool(dynamic raw); - - @protected - RsiLauncherAsarData dco_decode_box_autoadd_rsi_launcher_asar_data( - dynamic raw, - ); - - @protected - BigInt dco_decode_box_autoadd_u_64(dynamic raw); - - @protected - int dco_decode_i_32(dynamic raw); - - @protected - List dco_decode_list_String(dynamic raw); - - @protected - List dco_decode_list_prim_u_8_loose(dynamic raw); - - @protected - Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); - - @protected - List<(String, String)> dco_decode_list_record_string_string(dynamic raw); - - @protected - MyHttpVersion dco_decode_my_http_version(dynamic raw); - - @protected - MyMethod dco_decode_my_method(dynamic raw); - - @protected - Map? dco_decode_opt_Map_String_String_None(dynamic raw); - - @protected - String? dco_decode_opt_String(dynamic raw); - - @protected - bool? dco_decode_opt_box_autoadd_bool(dynamic raw); - - @protected - BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw); - - @protected - Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw); - - @protected - (String, String) dco_decode_record_string_string(dynamic raw); - - @protected - RsProcessStreamData dco_decode_rs_process_stream_data(dynamic raw); - - @protected - RsProcessStreamDataType dco_decode_rs_process_stream_data_type(dynamic raw); - - @protected - RsiLauncherAsarData dco_decode_rsi_launcher_asar_data(dynamic raw); - - @protected - RustHttpResponse dco_decode_rust_http_response(dynamic raw); - - @protected - int dco_decode_u_16(dynamic raw); - - @protected - int dco_decode_u_32(dynamic raw); - - @protected - BigInt dco_decode_u_64(dynamic raw); - - @protected - int dco_decode_u_8(dynamic raw); - - @protected - void dco_decode_unit(dynamic raw); - - @protected - AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); - - @protected - Map sse_decode_Map_String_String_None( - SseDeserializer deserializer, - ); - - @protected - RustStreamSink - sse_decode_StreamSink_rs_process_stream_data_Dco( - SseDeserializer deserializer, - ); - - @protected - String sse_decode_String(SseDeserializer deserializer); - - @protected - bool sse_decode_bool(SseDeserializer deserializer); - - @protected - bool sse_decode_box_autoadd_bool(SseDeserializer deserializer); - - @protected - RsiLauncherAsarData sse_decode_box_autoadd_rsi_launcher_asar_data( - SseDeserializer deserializer, - ); - - @protected - BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer); - - @protected - int sse_decode_i_32(SseDeserializer deserializer); - - @protected - List sse_decode_list_String(SseDeserializer deserializer); - - @protected - List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); - - @protected - Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); - - @protected - List<(String, String)> sse_decode_list_record_string_string( - SseDeserializer deserializer, - ); - - @protected - MyHttpVersion sse_decode_my_http_version(SseDeserializer deserializer); - - @protected - MyMethod sse_decode_my_method(SseDeserializer deserializer); - - @protected - Map? sse_decode_opt_Map_String_String_None( - SseDeserializer deserializer, - ); - - @protected - String? sse_decode_opt_String(SseDeserializer deserializer); - - @protected - bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer); - - @protected - BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer); - - @protected - Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer); - - @protected - (String, String) sse_decode_record_string_string( - SseDeserializer deserializer, - ); - - @protected - RsProcessStreamData sse_decode_rs_process_stream_data( - SseDeserializer deserializer, - ); - - @protected - RsProcessStreamDataType sse_decode_rs_process_stream_data_type( - SseDeserializer deserializer, - ); - - @protected - RsiLauncherAsarData sse_decode_rsi_launcher_asar_data( - SseDeserializer deserializer, - ); - - @protected - RustHttpResponse sse_decode_rust_http_response(SseDeserializer deserializer); - - @protected - int sse_decode_u_16(SseDeserializer deserializer); - - @protected - int sse_decode_u_32(SseDeserializer deserializer); - - @protected - BigInt sse_decode_u_64(SseDeserializer deserializer); - - @protected - int sse_decode_u_8(SseDeserializer deserializer); - - @protected - void sse_decode_unit(SseDeserializer deserializer); - - @protected - ffi.Pointer cst_encode_AnyhowException( - AnyhowException raw, - ) { - // Codec=Cst (C-struct based), see doc to use other codecs - throw UnimplementedError(); - } - - @protected - ffi.Pointer - cst_encode_Map_String_String_None(Map raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_list_record_string_string( - raw.entries.map((e) => (e.key, e.value)).toList(), - ); - } - - @protected - ffi.Pointer - cst_encode_StreamSink_rs_process_stream_data_Dco( - RustStreamSink raw, - ) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_String( - raw.setupAndSerialize( - codec: DcoCodec( - decodeSuccessData: dco_decode_rs_process_stream_data, - decodeErrorData: dco_decode_AnyhowException, - ), - ), - ); - } - - @protected - ffi.Pointer cst_encode_String(String raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return cst_encode_list_prim_u_8_strict(utf8.encoder.convert(raw)); - } - - @protected - ffi.Pointer cst_encode_box_autoadd_bool(bool raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return wire.cst_new_box_autoadd_bool(cst_encode_bool(raw)); - } - - @protected - ffi.Pointer - cst_encode_box_autoadd_rsi_launcher_asar_data(RsiLauncherAsarData raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - final ptr = wire.cst_new_box_autoadd_rsi_launcher_asar_data(); - cst_api_fill_to_wire_rsi_launcher_asar_data(raw, ptr.ref); - return ptr; - } - - @protected - ffi.Pointer cst_encode_box_autoadd_u_64(BigInt raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return wire.cst_new_box_autoadd_u_64(cst_encode_u_64(raw)); - } - - @protected - ffi.Pointer cst_encode_list_String(List raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - final ans = wire.cst_new_list_String(raw.length); - for (var i = 0; i < raw.length; ++i) { - ans.ref.ptr[i] = cst_encode_String(raw[i]); - } - return ans; - } - - @protected - ffi.Pointer cst_encode_list_prim_u_8_loose( - List raw, - ) { - // Codec=Cst (C-struct based), see doc to use other codecs - final ans = wire.cst_new_list_prim_u_8_loose(raw.length); - ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); - return ans; - } - - @protected - ffi.Pointer cst_encode_list_prim_u_8_strict( - Uint8List raw, - ) { - // Codec=Cst (C-struct based), see doc to use other codecs - final ans = wire.cst_new_list_prim_u_8_strict(raw.length); - ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); - return ans; - } - - @protected - ffi.Pointer - cst_encode_list_record_string_string(List<(String, String)> raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - final ans = wire.cst_new_list_record_string_string(raw.length); - for (var i = 0; i < raw.length; ++i) { - cst_api_fill_to_wire_record_string_string(raw[i], ans.ref.ptr[i]); - } - return ans; - } - - @protected - ffi.Pointer - cst_encode_opt_Map_String_String_None(Map? raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw == null ? ffi.nullptr : cst_encode_Map_String_String_None(raw); - } - - @protected - ffi.Pointer cst_encode_opt_String( - String? raw, - ) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw == null ? ffi.nullptr : cst_encode_String(raw); - } - - @protected - ffi.Pointer cst_encode_opt_box_autoadd_bool(bool? raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw == null ? ffi.nullptr : cst_encode_box_autoadd_bool(raw); - } - - @protected - ffi.Pointer cst_encode_opt_box_autoadd_u_64(BigInt? raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw == null ? ffi.nullptr : cst_encode_box_autoadd_u_64(raw); - } - - @protected - ffi.Pointer - cst_encode_opt_list_prim_u_8_strict(Uint8List? raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw == null ? ffi.nullptr : cst_encode_list_prim_u_8_strict(raw); - } - - @protected - int cst_encode_u_64(BigInt raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw.toSigned(64).toInt(); - } - - @protected - void cst_api_fill_to_wire_box_autoadd_rsi_launcher_asar_data( - RsiLauncherAsarData apiObj, - ffi.Pointer wireObj, - ) { - cst_api_fill_to_wire_rsi_launcher_asar_data(apiObj, wireObj.ref); - } - - @protected - void cst_api_fill_to_wire_record_string_string( - (String, String) apiObj, - wire_cst_record_string_string wireObj, - ) { - wireObj.field0 = cst_encode_String(apiObj.$1); - wireObj.field1 = cst_encode_String(apiObj.$2); - } - - @protected - void cst_api_fill_to_wire_rs_process_stream_data( - RsProcessStreamData apiObj, - wire_cst_rs_process_stream_data wireObj, - ) { - wireObj.data_type = cst_encode_rs_process_stream_data_type(apiObj.dataType); - wireObj.data = cst_encode_String(apiObj.data); - wireObj.rs_pid = cst_encode_u_32(apiObj.rsPid); - } - - @protected - void cst_api_fill_to_wire_rsi_launcher_asar_data( - RsiLauncherAsarData apiObj, - wire_cst_rsi_launcher_asar_data wireObj, - ) { - wireObj.asar_path = cst_encode_String(apiObj.asarPath); - wireObj.main_js_path = cst_encode_String(apiObj.mainJsPath); - wireObj.main_js_content = cst_encode_list_prim_u_8_strict( - apiObj.mainJsContent, - ); - } - - @protected - void cst_api_fill_to_wire_rust_http_response( - RustHttpResponse apiObj, - wire_cst_rust_http_response wireObj, - ) { - wireObj.status_code = cst_encode_u_16(apiObj.statusCode); - wireObj.headers = cst_encode_Map_String_String_None(apiObj.headers); - wireObj.url = cst_encode_String(apiObj.url); - wireObj.content_length = cst_encode_opt_box_autoadd_u_64( - apiObj.contentLength, - ); - wireObj.version = cst_encode_my_http_version(apiObj.version); - wireObj.remote_addr = cst_encode_String(apiObj.remoteAddr); - wireObj.data = cst_encode_opt_list_prim_u_8_strict(apiObj.data); - } - - @protected - bool cst_encode_bool(bool raw); - - @protected - int cst_encode_i_32(int raw); - - @protected - int cst_encode_my_http_version(MyHttpVersion raw); - - @protected - int cst_encode_my_method(MyMethod raw); - - @protected - int cst_encode_rs_process_stream_data_type(RsProcessStreamDataType raw); - - @protected - int cst_encode_u_16(int raw); - - @protected - int cst_encode_u_32(int raw); - - @protected - int cst_encode_u_8(int raw); - - @protected - void cst_encode_unit(void raw); - - @protected - void sse_encode_AnyhowException( - AnyhowException self, - SseSerializer serializer, - ); - - @protected - void sse_encode_Map_String_String_None( - Map self, - SseSerializer serializer, - ); - - @protected - void sse_encode_StreamSink_rs_process_stream_data_Dco( - RustStreamSink self, - SseSerializer serializer, - ); - - @protected - void sse_encode_String(String self, SseSerializer serializer); - - @protected - void sse_encode_bool(bool self, SseSerializer serializer); - - @protected - void sse_encode_box_autoadd_bool(bool self, SseSerializer serializer); - - @protected - void sse_encode_box_autoadd_rsi_launcher_asar_data( - RsiLauncherAsarData self, - SseSerializer serializer, - ); - - @protected - void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer); - - @protected - void sse_encode_i_32(int self, SseSerializer serializer); - - @protected - void sse_encode_list_String(List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); - - @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); - - @protected - void sse_encode_list_record_string_string( - List<(String, String)> self, - SseSerializer serializer, - ); - - @protected - void sse_encode_my_http_version(MyHttpVersion self, SseSerializer serializer); - - @protected - void sse_encode_my_method(MyMethod self, SseSerializer serializer); - - @protected - void sse_encode_opt_Map_String_String_None( - Map? self, - SseSerializer serializer, - ); - - @protected - void sse_encode_opt_String(String? self, SseSerializer serializer); - - @protected - void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer); - - @protected - void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); - - @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ); - - @protected - void sse_encode_record_string_string( - (String, String) self, - SseSerializer serializer, - ); - - @protected - void sse_encode_rs_process_stream_data( - RsProcessStreamData self, - SseSerializer serializer, - ); - - @protected - void sse_encode_rs_process_stream_data_type( - RsProcessStreamDataType self, - SseSerializer serializer, - ); - - @protected - void sse_encode_rsi_launcher_asar_data( - RsiLauncherAsarData self, - SseSerializer serializer, - ); - - @protected - void sse_encode_rust_http_response( - RustHttpResponse self, - SseSerializer serializer, - ); - - @protected - void sse_encode_u_16(int self, SseSerializer serializer); - - @protected - void sse_encode_u_32(int self, SseSerializer serializer); - - @protected - void sse_encode_u_64(BigInt self, SseSerializer serializer); - - @protected - void sse_encode_u_8(int self, SseSerializer serializer); - - @protected - void sse_encode_unit(void self, SseSerializer serializer); -} - -// Section: wire_class - -// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names -// AUTO GENERATED FILE, DO NOT EDIT. -// -// Generated by `package:ffigen`. -// ignore_for_file: type=lint - -/// generated by flutter_rust_bridge -class RustLibWire implements BaseWire { - factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => - RustLibWire(lib.ffiDynamicLibrary); - - /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; - - /// The symbols are looked up in [dynamicLibrary]. - RustLibWire(ffi.DynamicLibrary dynamicLibrary) - : _lookup = dynamicLibrary.lookup; - - /// The symbols are looked up with [lookup]. - RustLibWire.fromLookup( - ffi.Pointer Function(String symbolName) lookup, - ) : _lookup = lookup; - - void store_dart_post_cobject(DartPostCObjectFnType ptr) { - return _store_dart_post_cobject(ptr); - } - - late final _store_dart_post_cobjectPtr = - _lookup>( - 'store_dart_post_cobject', - ); - late final _store_dart_post_cobject = _store_dart_post_cobjectPtr - .asFunction(); - - void wire__crate__api__http_api__dns_lookup_ips( - int port_, - ffi.Pointer host, - ) { - return _wire__crate__api__http_api__dns_lookup_ips(port_, host); - } - - late final _wire__crate__api__http_api__dns_lookup_ipsPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__http_api__dns_lookup_ips'); - late final _wire__crate__api__http_api__dns_lookup_ips = - _wire__crate__api__http_api__dns_lookup_ipsPtr - .asFunction< - void Function(int, ffi.Pointer) - >(); - - void wire__crate__api__http_api__dns_lookup_txt( - int port_, - ffi.Pointer host, - ) { - return _wire__crate__api__http_api__dns_lookup_txt(port_, host); - } - - late final _wire__crate__api__http_api__dns_lookup_txtPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__http_api__dns_lookup_txt'); - late final _wire__crate__api__http_api__dns_lookup_txt = - _wire__crate__api__http_api__dns_lookup_txtPtr - .asFunction< - void Function(int, ffi.Pointer) - >(); - - void wire__crate__api__http_api__fetch( - int port_, - int method, - ffi.Pointer url, - ffi.Pointer headers, - ffi.Pointer input_data, - ffi.Pointer with_ip_address, - ffi.Pointer with_custom_dns, - ) { - return _wire__crate__api__http_api__fetch( - port_, - method, - url, - headers, - input_data, - with_ip_address, - with_custom_dns, - ); - } - - late final _wire__crate__api__http_api__fetchPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Int32, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__http_api__fetch'); - late final _wire__crate__api__http_api__fetch = - _wire__crate__api__http_api__fetchPtr - .asFunction< - void Function( - int, - int, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); - - void wire__crate__api__asar_api__get_rsi_launcher_asar_data( - int port_, - ffi.Pointer asar_path, - ) { - return _wire__crate__api__asar_api__get_rsi_launcher_asar_data( - port_, - asar_path, - ); - } - - late final _wire__crate__api__asar_api__get_rsi_launcher_asar_dataPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ) - > - >( - 'frbgen_starcitizen_doctor_wire__crate__api__asar_api__get_rsi_launcher_asar_data', - ); - late final _wire__crate__api__asar_api__get_rsi_launcher_asar_data = - _wire__crate__api__asar_api__get_rsi_launcher_asar_dataPtr - .asFunction< - void Function(int, ffi.Pointer) - >(); - - void wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js( - int port_, - ffi.Pointer that, - ffi.Pointer content, - ) { - return _wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js( - port_, - that, - content, - ); - } - - late final _wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_jsPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ffi.Pointer, - ) - > - >( - 'frbgen_starcitizen_doctor_wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js', - ); - late final _wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js = - _wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_jsPtr - .asFunction< - void Function( - int, - ffi.Pointer, - ffi.Pointer, - ) - >(); - - void wire__crate__api__win32_api__send_notify( - int port_, - ffi.Pointer summary, - ffi.Pointer body, - ffi.Pointer app_name, - ffi.Pointer app_id, - ) { - return _wire__crate__api__win32_api__send_notify( - port_, - summary, - body, - app_name, - app_id, - ); - } - - late final _wire__crate__api__win32_api__send_notifyPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__win32_api__send_notify'); - late final _wire__crate__api__win32_api__send_notify = - _wire__crate__api__win32_api__send_notifyPtr - .asFunction< - void Function( - int, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); - - void wire__crate__api__http_api__set_default_header( - int port_, - ffi.Pointer headers, - ) { - return _wire__crate__api__http_api__set_default_header(port_, headers); - } - - late final _wire__crate__api__http_api__set_default_headerPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ) - > - >( - 'frbgen_starcitizen_doctor_wire__crate__api__http_api__set_default_header', - ); - late final _wire__crate__api__http_api__set_default_header = - _wire__crate__api__http_api__set_default_headerPtr - .asFunction< - void Function(int, ffi.Pointer) - >(); - - void wire__crate__api__win32_api__set_foreground_window( - int port_, - ffi.Pointer window_name, - ) { - return _wire__crate__api__win32_api__set_foreground_window( - port_, - window_name, - ); - } - - late final _wire__crate__api__win32_api__set_foreground_windowPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ) - > - >( - 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__set_foreground_window', - ); - late final _wire__crate__api__win32_api__set_foreground_window = - _wire__crate__api__win32_api__set_foreground_windowPtr - .asFunction< - void Function(int, ffi.Pointer) - >(); - - void wire__crate__api__rs_process__start( - int port_, - ffi.Pointer executable, - ffi.Pointer arguments, - ffi.Pointer working_directory, - ffi.Pointer stream_sink, - ) { - return _wire__crate__api__rs_process__start( - port_, - executable, - arguments, - working_directory, - stream_sink, - ); - } - - late final _wire__crate__api__rs_process__startPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__rs_process__start'); - late final _wire__crate__api__rs_process__start = - _wire__crate__api__rs_process__startPtr - .asFunction< - void Function( - int, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); - - void wire__crate__api__rs_process__write( - int port_, - int rs_pid, - ffi.Pointer data, - ) { - return _wire__crate__api__rs_process__write(port_, rs_pid, data); - } - - late final _wire__crate__api__rs_process__writePtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, - ffi.Uint32, - ffi.Pointer, - ) - > - >('frbgen_starcitizen_doctor_wire__crate__api__rs_process__write'); - late final _wire__crate__api__rs_process__write = - _wire__crate__api__rs_process__writePtr - .asFunction< - void Function(int, int, ffi.Pointer) - >(); - - ffi.Pointer cst_new_box_autoadd_bool(bool value) { - return _cst_new_box_autoadd_bool(value); - } - - late final _cst_new_box_autoadd_boolPtr = - _lookup Function(ffi.Bool)>>( - 'frbgen_starcitizen_doctor_cst_new_box_autoadd_bool', - ); - late final _cst_new_box_autoadd_bool = _cst_new_box_autoadd_boolPtr - .asFunction Function(bool)>(); - - ffi.Pointer - cst_new_box_autoadd_rsi_launcher_asar_data() { - return _cst_new_box_autoadd_rsi_launcher_asar_data(); - } - - late final _cst_new_box_autoadd_rsi_launcher_asar_dataPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function() - > - >('frbgen_starcitizen_doctor_cst_new_box_autoadd_rsi_launcher_asar_data'); - late final _cst_new_box_autoadd_rsi_launcher_asar_data = - _cst_new_box_autoadd_rsi_launcher_asar_dataPtr - .asFunction< - ffi.Pointer Function() - >(); - - ffi.Pointer cst_new_box_autoadd_u_64(int value) { - return _cst_new_box_autoadd_u_64(value); - } - - late final _cst_new_box_autoadd_u_64Ptr = - _lookup Function(ffi.Uint64)>>( - 'frbgen_starcitizen_doctor_cst_new_box_autoadd_u_64', - ); - late final _cst_new_box_autoadd_u_64 = _cst_new_box_autoadd_u_64Ptr - .asFunction Function(int)>(); - - ffi.Pointer cst_new_list_String(int len) { - return _cst_new_list_String(len); - } - - late final _cst_new_list_StringPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Int32) - > - >('frbgen_starcitizen_doctor_cst_new_list_String'); - late final _cst_new_list_String = _cst_new_list_StringPtr - .asFunction Function(int)>(); - - ffi.Pointer cst_new_list_prim_u_8_loose( - int len, - ) { - return _cst_new_list_prim_u_8_loose(len); - } - - late final _cst_new_list_prim_u_8_loosePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Int32) - > - >('frbgen_starcitizen_doctor_cst_new_list_prim_u_8_loose'); - late final _cst_new_list_prim_u_8_loose = _cst_new_list_prim_u_8_loosePtr - .asFunction Function(int)>(); - - ffi.Pointer cst_new_list_prim_u_8_strict( - int len, - ) { - return _cst_new_list_prim_u_8_strict(len); - } - - late final _cst_new_list_prim_u_8_strictPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Int32) - > - >('frbgen_starcitizen_doctor_cst_new_list_prim_u_8_strict'); - late final _cst_new_list_prim_u_8_strict = _cst_new_list_prim_u_8_strictPtr - .asFunction Function(int)>(); - - ffi.Pointer - cst_new_list_record_string_string(int len) { - return _cst_new_list_record_string_string(len); - } - - late final _cst_new_list_record_string_stringPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Int32) - > - >('frbgen_starcitizen_doctor_cst_new_list_record_string_string'); - late final _cst_new_list_record_string_string = - _cst_new_list_record_string_stringPtr - .asFunction< - ffi.Pointer Function(int) - >(); - - int dummy_method_to_enforce_bundling() { - return _dummy_method_to_enforce_bundling(); - } - - late final _dummy_method_to_enforce_bundlingPtr = - _lookup>( - 'dummy_method_to_enforce_bundling', - ); - late final _dummy_method_to_enforce_bundling = - _dummy_method_to_enforce_bundlingPtr.asFunction(); -} - -typedef DartPort = ffi.Int64; -typedef DartDartPort = int; -typedef DartPostCObjectFnTypeFunction = - ffi.Bool Function(DartPort port_id, ffi.Pointer message); -typedef DartDartPostCObjectFnTypeFunction = - bool Function(DartDartPort port_id, ffi.Pointer message); -typedef DartPostCObjectFnType = - ffi.Pointer>; - -final class wire_cst_list_prim_u_8_strict extends ffi.Struct { - external ffi.Pointer ptr; - - @ffi.Int32() - external int len; -} - -final class wire_cst_record_string_string extends ffi.Struct { - external ffi.Pointer field0; - - external ffi.Pointer field1; -} - -final class wire_cst_list_record_string_string extends ffi.Struct { - external ffi.Pointer ptr; - - @ffi.Int32() - external int len; -} - -final class wire_cst_rsi_launcher_asar_data extends ffi.Struct { - external ffi.Pointer asar_path; - - external ffi.Pointer main_js_path; - - external ffi.Pointer main_js_content; -} - -final class wire_cst_list_prim_u_8_loose extends ffi.Struct { - external ffi.Pointer ptr; - - @ffi.Int32() - external int len; -} - -final class wire_cst_list_String extends ffi.Struct { - external ffi.Pointer> ptr; - - @ffi.Int32() - external int len; -} - -final class wire_cst_rs_process_stream_data extends ffi.Struct { - @ffi.Int32() - external int data_type; - - external ffi.Pointer data; - - @ffi.Uint32() - external int rs_pid; -} - -final class wire_cst_rust_http_response extends ffi.Struct { - @ffi.Uint16() - external int status_code; - - external ffi.Pointer headers; - - external ffi.Pointer url; - - external ffi.Pointer content_length; - - @ffi.Int32() - external int version; - - external ffi.Pointer remote_addr; - - external ffi.Pointer data; -} diff --git a/lib/common/rust/http_package.dart b/lib/common/rust/http_package.dart index 6ab82bc..d72b0a1 100644 --- a/lib/common/rust/http_package.dart +++ b/lib/common/rust/http_package.dart @@ -1,10 +1,7 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. +// HTTP 响应数据类 +// 原先由 flutter_rust_bridge 生成,现改为纯 Dart 实现 -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import - -import 'frb_generated.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'dart:typed_data'; enum MyHttpVersion { http09, http10, http11, http2, http3, httpUnknown } diff --git a/lib/common/utils/file_cache_utils_stub.dart b/lib/common/utils/file_cache_utils_stub.dart new file mode 100644 index 0000000..47c36ea --- /dev/null +++ b/lib/common/utils/file_cache_utils_stub.dart @@ -0,0 +1,14 @@ +// Stub file for web platform - not used +class FileCacheUtils { + static Future getFile(String url) async { + throw UnsupportedError('File operations are not supported on web platform'); + } + + static Future clearCache(String url) async { + throw UnsupportedError('File operations are not supported on web platform'); + } + + static Future clearAllCache() async { + throw UnsupportedError('File operations are not supported on web platform'); + } +} diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 8799ff1..d63ec5e 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -366,6 +366,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_action_rsi_launcher_log": MessageLookupByLibrary.simpleMessage( "RSI Launcher log", ), + "doctor_action_select_log_file": MessageLookupByLibrary.simpleMessage( + "Select Log File", + ), "doctor_action_tip_checking_game_log": MessageLookupByLibrary.simpleMessage( "Checking: Game.log", ), @@ -435,6 +438,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_game_rescue_service_note": MessageLookupByLibrary.simpleMessage( "You are about to access the game anomaly rescue service provided by Deep Space Treatment Center (QQ Group: 536454632), which mainly solves game installation failures and frequent crashes. Please do not join the group for gameplay issues.", ), + "doctor_info_log_file_selected": MessageLookupByLibrary.simpleMessage( + "Log file selected", + ), "doctor_info_need_help": MessageLookupByLibrary.simpleMessage( "Need help? Click to join the group for free human support!", ), @@ -491,6 +497,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_tool_check_result_note": MessageLookupByLibrary.simpleMessage( "Note: The detection results of this tool are for reference only. If you do not understand the following operations, please provide screenshots to experienced players!", ), + "doctor_info_web_select_log_file": MessageLookupByLibrary.simpleMessage( + "Web platform: Please manually select the Game.log file for diagnosis", + ), "doctor_tip_title_select_game_directory": MessageLookupByLibrary.simpleMessage( "Please select the game installation directory on the home page.", diff --git a/lib/generated/intl/messages_ja.dart b/lib/generated/intl/messages_ja.dart index 40075d5..ff3b6d2 100644 --- a/lib/generated/intl/messages_ja.dart +++ b/lib/generated/intl/messages_ja.dart @@ -344,6 +344,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_action_rsi_launcher_log": MessageLookupByLibrary.simpleMessage( "RSIランチャーログ", ), + "doctor_action_select_log_file": MessageLookupByLibrary.simpleMessage( + "ログファイルを選択", + ), "doctor_action_tip_checking_game_log": MessageLookupByLibrary.simpleMessage( "確認中:Game.log", ), @@ -408,6 +411,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_game_rescue_service_note": MessageLookupByLibrary.simpleMessage( "深宇宙治療センター(QQグループ番号:536454632)によるゲーム異常救済サービスにアクセスしようとしています。これは主にゲームのインストール失敗や頻繁なクラッシュに対応するものです。ゲームプレイの問題に関しては、グループに参加しないでください。", ), + "doctor_info_log_file_selected": MessageLookupByLibrary.simpleMessage( + "ログファイルが選択されました", + ), "doctor_info_need_help": MessageLookupByLibrary.simpleMessage( "助けが必要ですか?クリックしてグループに参加し、無料のサポートを受けましょう!", ), @@ -463,6 +469,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_tool_check_result_note": MessageLookupByLibrary.simpleMessage( "注意:このツールの検出結果は参考用です。以下の操作が理解できない場合は、スクリーンショットを経験豊富なプレイヤーに提供してください!", ), + "doctor_info_web_select_log_file": MessageLookupByLibrary.simpleMessage( + "Webプラットフォーム:Game.logファイルを手動で選択して診断してください", + ), "doctor_tip_title_select_game_directory": MessageLookupByLibrary.simpleMessage( "ホームページでゲームインストールディレクトリを選択してください。", diff --git a/lib/generated/intl/messages_ru.dart b/lib/generated/intl/messages_ru.dart index dd2a4d6..4524191 100644 --- a/lib/generated/intl/messages_ru.dart +++ b/lib/generated/intl/messages_ru.dart @@ -371,6 +371,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_action_rsi_launcher_log": MessageLookupByLibrary.simpleMessage( "Лог RSI Launcher", ), + "doctor_action_select_log_file": MessageLookupByLibrary.simpleMessage( + "Выбрать файл журнала", + ), "doctor_action_tip_checking_game_log": MessageLookupByLibrary.simpleMessage( "Проверка: Game.log", ), @@ -440,6 +443,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_game_rescue_service_note": MessageLookupByLibrary.simpleMessage( "Вы собираетесь перейти к сервису спасения игры, предоставляемому Центром глубокого космического лечения (QQ группа: 536454632), который в основном решает проблемы с неудачной установкой и частыми вылетами. Не присоединяйтесь к группе для вопросов по игровому процессу.", ), + "doctor_info_log_file_selected": MessageLookupByLibrary.simpleMessage( + "Файл журнала выбран", + ), "doctor_info_need_help": MessageLookupByLibrary.simpleMessage( "Нужна помощь? Нажмите, чтобы присоединиться к группе для бесплатной поддержки!", ), @@ -496,6 +502,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_tool_check_result_note": MessageLookupByLibrary.simpleMessage( "Примечание: Результаты проверки этим инструментом предоставляются только для справки. Если вы не понимаете следующие операции, пожалуйста, предоставьте снимок экрана опытным игрокам!", ), + "doctor_info_web_select_log_file": MessageLookupByLibrary.simpleMessage( + "Web-платформа: Пожалуйста, выберите файл Game.log вручную для диагностики", + ), "doctor_tip_title_select_game_directory": MessageLookupByLibrary.simpleMessage( "Пожалуйста, выберите директорию установки игры на главной странице.", diff --git a/lib/generated/intl/messages_zh_CN.dart b/lib/generated/intl/messages_zh_CN.dart index 56dc909..68f3b25 100644 --- a/lib/generated/intl/messages_zh_CN.dart +++ b/lib/generated/intl/messages_zh_CN.dart @@ -320,6 +320,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_action_rsi_launcher_log": MessageLookupByLibrary.simpleMessage( "RSI启动器log", ), + "doctor_action_select_log_file": MessageLookupByLibrary.simpleMessage( + "选择 log 文件", + ), "doctor_action_tip_checking_game_log": MessageLookupByLibrary.simpleMessage( "正在检查:Game.log", ), @@ -381,6 +384,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_game_rescue_service_note": MessageLookupByLibrary.simpleMessage( "您即将前往由 深空治疗中心(QQ群号:536454632 ) 提供的游戏异常救援服务,主要解决游戏安装失败与频繁闪退,如游戏玩法问题,请勿加群。", ), + "doctor_info_log_file_selected": MessageLookupByLibrary.simpleMessage( + "已选择日志文件", + ), "doctor_info_need_help": MessageLookupByLibrary.simpleMessage( "需要帮助? 点击加群寻求免费人工支援!", ), @@ -432,6 +438,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_tool_check_result_note": MessageLookupByLibrary.simpleMessage( "注意:本工具检测结果仅供参考,若您不理解以下操作,请提供截图给有经验的玩家!", ), + "doctor_info_web_select_log_file": MessageLookupByLibrary.simpleMessage( + "Web 平台请手动选择 Game.log 文件进行诊断", + ), "doctor_tip_title_select_game_directory": MessageLookupByLibrary.simpleMessage("请在首页选择游戏安装目录。"), "doctor_title_one_click_diagnosis": m18, diff --git a/lib/generated/intl/messages_zh_TW.dart b/lib/generated/intl/messages_zh_TW.dart index 6e9f1cd..7194e6a 100644 --- a/lib/generated/intl/messages_zh_TW.dart +++ b/lib/generated/intl/messages_zh_TW.dart @@ -322,6 +322,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_action_rsi_launcher_log": MessageLookupByLibrary.simpleMessage( "RSI啟動器log", ), + "doctor_action_select_log_file": MessageLookupByLibrary.simpleMessage( + "選擇 log 檔案", + ), "doctor_action_tip_checking_game_log": MessageLookupByLibrary.simpleMessage( "正在檢查:Game.log", ), @@ -387,6 +390,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_game_rescue_service_note": MessageLookupByLibrary.simpleMessage( "您即將前往由 深空治療中心(QQ群號:536454632 ) 提供的遊戲異常救援服務,主要解決遊戲安裝失敗與頻繁閃退,如遊戲玩法問題,請勿加群。", ), + "doctor_info_log_file_selected": MessageLookupByLibrary.simpleMessage( + "已選擇日誌檔案", + ), "doctor_info_need_help": MessageLookupByLibrary.simpleMessage( "需要幫助? 點擊加群尋求免費人工支援!", ), @@ -438,6 +444,9 @@ class MessageLookup extends MessageLookupByLibrary { "doctor_info_tool_check_result_note": MessageLookupByLibrary.simpleMessage( "注意:本工具檢測結果僅供參考,若您不理解以下操作,請提供截圖給有經驗的玩家!", ), + "doctor_info_web_select_log_file": MessageLookupByLibrary.simpleMessage( + "Web 平台請手動選擇 Game.log 檔案進行診斷", + ), "doctor_tip_title_select_game_directory": MessageLookupByLibrary.simpleMessage("請在首頁選擇遊戲安裝目錄。"), "doctor_title_one_click_diagnosis": m18, diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index d5a941c..6bcd169 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -28,10 +28,9 @@ class S { static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = - (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; @@ -745,6 +744,36 @@ class S { ); } + /// `Select Log File` + String get doctor_action_select_log_file { + return Intl.message( + 'Select Log File', + name: 'doctor_action_select_log_file', + desc: '', + args: [], + ); + } + + /// `Web platform: Please manually select the Game.log file for diagnosis` + String get doctor_info_web_select_log_file { + return Intl.message( + 'Web platform: Please manually select the Game.log file for diagnosis', + name: 'doctor_info_web_select_log_file', + desc: '', + args: [], + ); + } + + /// `Log file selected` + String get doctor_info_log_file_selected { + return Intl.message( + 'Log file selected', + name: 'doctor_info_log_file_selected', + desc: '', + args: [], + ); + } + /// `Scan complete, no issues found!` String get doctor_info_scan_complete_no_issues { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e734660..f3ea1a4 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -141,6 +141,12 @@ "@doctor_action_rsi_launcher_log": {}, "doctor_action_game_run_log": "Game run log", "@doctor_action_game_run_log": {}, + "doctor_action_select_log_file": "Select Log File", + "@doctor_action_select_log_file": {}, + "doctor_info_web_select_log_file": "Web platform: Please manually select the Game.log file for diagnosis", + "@doctor_info_web_select_log_file": {}, + "doctor_info_log_file_selected": "Log file selected", + "@doctor_info_log_file_selected": {}, "doctor_info_scan_complete_no_issues": "Scan complete, no issues found!", "@doctor_info_scan_complete_no_issues": {}, "doctor_info_processing": "Processing...", diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index 52492b4..f0e2974 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -141,6 +141,12 @@ "@doctor_action_rsi_launcher_log": {}, "doctor_action_game_run_log": "ゲーム実行ログ", "@doctor_action_game_run_log": {}, + "doctor_action_select_log_file": "ログファイルを選択", + "@doctor_action_select_log_file": {}, + "doctor_info_web_select_log_file": "Webプラットフォーム:Game.logファイルを手動で選択して診断してください", + "@doctor_info_web_select_log_file": {}, + "doctor_info_log_file_selected": "ログファイルが選択されました", + "@doctor_info_log_file_selected": {}, "doctor_info_scan_complete_no_issues": "スキャン完了、問題は見つかりませんでした!", "@doctor_info_scan_complete_no_issues": {}, "doctor_info_processing": "処理中...", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 382ab04..bff748d 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -141,6 +141,12 @@ "@doctor_action_rsi_launcher_log": {}, "doctor_action_game_run_log": "Лог запуска игры", "@doctor_action_game_run_log": {}, + "doctor_action_select_log_file": "Выбрать файл журнала", + "@doctor_action_select_log_file": {}, + "doctor_info_web_select_log_file": "Web-платформа: Пожалуйста, выберите файл Game.log вручную для диагностики", + "@doctor_info_web_select_log_file": {}, + "doctor_info_log_file_selected": "Файл журнала выбран", + "@doctor_info_log_file_selected": {}, "doctor_info_scan_complete_no_issues": "Сканирование завершено, проблем не обнаружено!", "@doctor_info_scan_complete_no_issues": {}, "doctor_info_processing": "Обработка...", diff --git a/lib/l10n/intl_zh_CN.arb b/lib/l10n/intl_zh_CN.arb index 2faad7c..2e2db29 100644 --- a/lib/l10n/intl_zh_CN.arb +++ b/lib/l10n/intl_zh_CN.arb @@ -140,6 +140,12 @@ "@doctor_action_rsi_launcher_log": {}, "doctor_action_game_run_log": "游戏运行log", "@doctor_action_game_run_log": {}, + "doctor_action_select_log_file": "选择 log 文件", + "@doctor_action_select_log_file": {}, + "doctor_info_web_select_log_file": "Web 平台请手动选择 Game.log 文件进行诊断", + "@doctor_info_web_select_log_file": {}, + "doctor_info_log_file_selected": "已选择日志文件", + "@doctor_info_log_file_selected": {}, "doctor_info_scan_complete_no_issues": "扫描完毕,没有找到问题!", "@doctor_info_scan_complete_no_issues": {}, "doctor_info_processing": "正在处理...", diff --git a/lib/l10n/intl_zh_TW.arb b/lib/l10n/intl_zh_TW.arb index 56ddbf4..7827ce5 100644 --- a/lib/l10n/intl_zh_TW.arb +++ b/lib/l10n/intl_zh_TW.arb @@ -140,6 +140,12 @@ "@doctor_action_rsi_launcher_log": {}, "doctor_action_game_run_log": "遊戲執行log", "@doctor_action_game_run_log": {}, + "doctor_action_select_log_file": "選擇 log 檔案", + "@doctor_action_select_log_file": {}, + "doctor_info_web_select_log_file": "Web 平台請手動選擇 Game.log 檔案進行診斷", + "@doctor_info_web_select_log_file": {}, + "doctor_info_log_file_selected": "已選擇日誌檔案", + "@doctor_info_log_file_selected": {}, "doctor_info_scan_complete_no_issues": "掃描完畢,沒有找到問題!", "@doctor_info_scan_complete_no_issues": {}, "doctor_info_processing": "正在處理...", diff --git a/lib/main.dart b/lib/main.dart index 5299e5f..f67120f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_webview_window/desktop_webview_window.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/generated/l10n.dart'; @@ -12,9 +13,20 @@ import 'app.dart'; import 'common/utils/multi_window_manager.dart'; void main(List args) async { + if (kIsWeb) { + // Web 版本直接运行 + WidgetsFlutterBinding.ensureInitialized(); + runApp(const ProviderScope(child: App())); + return; + } + + // Desktop 版本 // webview window - if (runWebViewTitleBarWidget(args, - backgroundColor: const Color.fromRGBO(19, 36, 49, 1), builder: _defaultWebviewTitleBar)) { + if (runWebViewTitleBarWidget( + args, + backgroundColor: const Color.fromRGBO(19, 36, 49, 1), + builder: _defaultWebviewTitleBar, + )) { return; } if (args.firstOrNull == 'multi_window') { @@ -29,10 +41,7 @@ void main(List args) async { Future _initWindow() async { await windowManager.ensureInitialized(); - await windowManager.setTitleBarStyle( - TitleBarStyle.hidden, - windowButtonVisibility: false, - ); + await windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: false); await windowManager.setSize(const Size(1280, 810)); await windowManager.setMinimumSize(const Size(1280, 810)); await windowManager.center(animate: true); @@ -47,10 +56,14 @@ class App extends HookConsumerWidget with WindowListener { final appState = ref.watch(appGlobalModelProvider); useEffect(() { - windowManager.addListener(this); - windowManager.setPreventClose(true); + if (!kIsWeb) { + windowManager.addListener(this); + windowManager.setPreventClose(true); + } return () async { - windowManager.removeListener(this); + if (!kIsWeb) { + windowManager.removeListener(this); + } }; }, const []); @@ -73,18 +86,22 @@ class App extends HookConsumerWidget with WindowListener { ); }, theme: FluentThemeData( - brightness: Brightness.dark, - fontFamily: "SourceHanSansCN-Regular", - navigationPaneTheme: NavigationPaneThemeData( - backgroundColor: appState.themeConf.backgroundColor, + brightness: Brightness.dark, + fontFamily: "SourceHanSansCN-Regular", + navigationPaneTheme: NavigationPaneThemeData(backgroundColor: appState.themeConf.backgroundColor), + menuColor: appState.themeConf.menuColor, + micaBackgroundColor: appState.themeConf.micaColor, + buttonTheme: ButtonThemeData( + defaultButtonStyle: ButtonStyle( + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + side: BorderSide(color: Colors.white.withValues(alpha: .01)), + ), + ), ), - menuColor: appState.themeConf.menuColor, - micaBackgroundColor: appState.themeConf.micaColor, - buttonTheme: ButtonThemeData( - defaultButtonStyle: ButtonStyle( - shape: WidgetStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), side: BorderSide(color: Colors.white.withValues(alpha: .01)))), - ))), + ), + ), locale: appState.appLocale, debugShowCheckedModeBanner: false, routeInformationParser: router.routeInformationParser, @@ -96,7 +113,7 @@ class App extends HookConsumerWidget with WindowListener { @override Future onWindowClose() async { debugPrint("onWindowClose"); - if (await windowManager.isPreventClose()) { + if (!kIsWeb && await windowManager.isPreventClose()) { final windows = await DesktopMultiWindow.getAllSubWindowIds(); for (final id in windows) { await WindowController.fromWindowId(id).close(); @@ -112,42 +129,28 @@ Widget _defaultWebviewTitleBar(BuildContext context) { final state = TitleBarWebViewState.of(context); final controller = TitleBarWebViewController.of(context); return FluentTheme( - data: FluentThemeData.dark(), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (Platform.isMacOS) const SizedBox(width: 96), - IconButton( - onPressed: !state.canGoBack ? null : controller.back, - icon: const Icon(FluentIcons.chevron_left), - ), - const SizedBox(width: 12), - IconButton( - onPressed: !state.canGoForward ? null : controller.forward, - icon: const Icon(FluentIcons.chevron_right), - ), - const SizedBox(width: 12), - if (state.isLoading) - IconButton( - onPressed: controller.stop, - icon: const Icon(FluentIcons.chrome_close), - ) - else - IconButton( - onPressed: controller.reload, - icon: const Icon(FluentIcons.refresh), - ), - const SizedBox(width: 12), - (state.isLoading) - ? const SizedBox( - width: 24, - height: 24, - child: ProgressRing(), - ) - : const SizedBox(width: 24), - const SizedBox(width: 12), - SelectableText(state.url ?? ""), - const Spacer() - ], - )); + data: FluentThemeData.dark(), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (Platform.isMacOS) const SizedBox(width: 96), + IconButton(onPressed: !state.canGoBack ? null : controller.back, icon: const Icon(FluentIcons.chevron_left)), + const SizedBox(width: 12), + IconButton( + onPressed: !state.canGoForward ? null : controller.forward, + icon: const Icon(FluentIcons.chevron_right), + ), + const SizedBox(width: 12), + if (state.isLoading) + IconButton(onPressed: controller.stop, icon: const Icon(FluentIcons.chrome_close)) + else + IconButton(onPressed: controller.reload, icon: const Icon(FluentIcons.refresh)), + const SizedBox(width: 12), + (state.isLoading) ? const SizedBox(width: 24, height: 24, child: ProgressRing()) : const SizedBox(width: 24), + const SizedBox(width: 12), + SelectableText(state.url ?? ""), + const Spacer(), + ], + ), + ); } diff --git a/lib/provider/aria2c.dart b/lib/provider/aria2c.dart index 3e4f75a..169505d 100644 --- a/lib/provider/aria2c.dart +++ b/lib/provider/aria2c.dart @@ -5,11 +5,8 @@ import 'dart:io'; import 'dart:math'; import 'package:aria2/aria2.dart'; import 'package:flutter/foundation.dart'; -import 'package:hive_ce/hive.dart'; import 'package:starcitizen_doctor/api/api.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; -import 'package:starcitizen_doctor/common/rust/api/rs_process.dart' - as rs_process; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/provider.dart'; @@ -20,11 +17,8 @@ part 'aria2c.freezed.dart'; @freezed abstract class Aria2cModelState with _$Aria2cModelState { - const factory Aria2cModelState({ - required String aria2cDir, - Aria2c? aria2c, - Aria2GlobalStat? aria2globalStat, - }) = _Aria2cModelState; + const factory Aria2cModelState({required String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat}) = + _Aria2cModelState; } extension Aria2cModelExt on Aria2cModelState { @@ -32,24 +26,17 @@ extension Aria2cModelExt on Aria2cModelState { bool get hasDownloadTask => aria2globalStat != null && aria2TotalTaskNum > 0; - int get aria2TotalTaskNum => aria2globalStat == null - ? 0 - : ((aria2globalStat!.numActive ?? 0) + - (aria2globalStat!.numWaiting ?? 0)); + int get aria2TotalTaskNum => + aria2globalStat == null ? 0 : ((aria2globalStat!.numActive ?? 0) + (aria2globalStat!.numWaiting ?? 0)); } @riverpod class Aria2cModel extends _$Aria2cModel { - bool _disposed = false; - @override Aria2cModelState build() { if (appGlobalState.applicationBinaryModuleDir == null) { throw Exception("applicationBinaryModuleDir is null"); } - ref.onDispose(() { - _disposed = true; - }); ref.keepAlive(); final aria2cDir = "${appGlobalState.applicationBinaryModuleDir}\\aria2c"; // LazyLoad init @@ -57,8 +44,7 @@ class Aria2cModel extends _$Aria2cModel { try { final sessionFile = File("$aria2cDir\\aria2.session"); // 有下载任务则第一时间初始化 - if (await sessionFile.exists() && - (await sessionFile.readAsString()).trim().isNotEmpty) { + if (await sessionFile.exists() && (await sessionFile.readAsString()).trim().isNotEmpty) { dPrint("launch Aria2c daemon"); await launchDaemon(appGlobalState.applicationBinaryModuleDir!); } else { @@ -74,8 +60,7 @@ class Aria2cModel extends _$Aria2cModel { Future launchDaemon(String applicationBinaryModuleDir) async { if (state.aria2c != null) return; - await BinaryModuleConf.extractModule( - ["aria2c"], applicationBinaryModuleDir); + await BinaryModuleConf.extractModule(["aria2c"], applicationBinaryModuleDir); /// skip for debug hot reload if (kDebugMode) { @@ -90,7 +75,7 @@ class Aria2cModel extends _$Aria2cModel { await sessionFile.create(recursive: true); } - final exePath = "${state.aria2cDir}\\aria2c.exe"; + // final exePath = "${state.aria2cDir}\\aria2c.exe"; final port = await getFreePort(); final pwd = generateRandomPassword(16); dPrint("pwd === $pwd"); @@ -98,53 +83,53 @@ class Aria2cModel extends _$Aria2cModel { dPrint("trackerList === $trackerList"); dPrint("Aria2cManager .----- aria2c start $port------"); - final stream = rs_process.start( - executable: exePath, - arguments: [ - "-V", - "-c", - "-x 16", - "--dir=${state.aria2cDir}\\downloads", - "--disable-ipv6", - "--enable-rpc", - "--pause", - "--rpc-listen-port=$port", - "--rpc-secret=$pwd", - "--input-file=${sessionFile.absolute.path.trim()}", - "--save-session=${sessionFile.absolute.path.trim()}", - "--save-session-interval=60", - "--file-allocation=trunc", - "--seed-time=0", - ], - workingDirectory: state.aria2cDir); + // final stream = rs_process.start( + // executable: exePath, + // arguments: [ + // "-V", + // "-c", + // "-x 16", + // "--dir=${state.aria2cDir}\\downloads", + // "--disable-ipv6", + // "--enable-rpc", + // "--pause", + // "--rpc-listen-port=$port", + // "--rpc-secret=$pwd", + // "--input-file=${sessionFile.absolute.path.trim()}", + // "--save-session=${sessionFile.absolute.path.trim()}", + // "--save-session-interval=60", + // "--file-allocation=trunc", + // "--seed-time=0", + // ], + // workingDirectory: state.aria2cDir, + // ); - String launchError = ""; + // String launchError = ""; - stream.listen((event) { - dPrint( - "Aria2cManager.rs_process event === [${event.rsPid}] ${event.dataType} >> ${event.data}"); - switch (event.dataType) { - case rs_process.RsProcessStreamDataType.output: - if (event.data.contains("IPv4 RPC: listening on TCP port")) { - _onLaunch(port, pwd, trackerList); - } - break; - case rs_process.RsProcessStreamDataType.error: - launchError = event.data; - state = state.copyWith(aria2c: null); - break; - case rs_process.RsProcessStreamDataType.exit: - launchError = event.data; - state = state.copyWith(aria2c: null); - break; - } - }); + // stream.listen((event) { + // dPrint("Aria2cManager.rs_process event === [${event.rsPid}] ${event.dataType} >> ${event.data}"); + // switch (event.dataType) { + // case rs_process.RsProcessStreamDataType.output: + // if (event.data.contains("IPv4 RPC: listening on TCP port")) { + // _onLaunch(port, pwd, trackerList); + // } + // break; + // case rs_process.RsProcessStreamDataType.error: + // launchError = event.data; + // state = state.copyWith(aria2c: null); + // break; + // case rs_process.RsProcessStreamDataType.exit: + // launchError = event.data; + // state = state.copyWith(aria2c: null); + // break; + // } + // }); - while (true) { - if (state.aria2c != null) return; - if (launchError.isNotEmpty) throw launchError; - await Future.delayed(const Duration(milliseconds: 100)); - } + // while (true) { + // if (state.aria2c != null) return; + // if (launchError.isNotEmpty) throw launchError; + // await Future.delayed(const Duration(milliseconds: 100)); + // } } Future getFreePort() async { @@ -155,8 +140,7 @@ class Aria2cModel extends _$Aria2cModel { } String generateRandomPassword(int length) { - const String charset = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + const String charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random = Random(); StringBuffer buffer = StringBuffer(); for (int i = 0; i < length; i++) { @@ -182,36 +166,37 @@ class Aria2cModel extends _$Aria2cModel { return 0; } - Future _onLaunch(int port, String pwd, String trackerList) async { - final aria2c = Aria2c("ws://127.0.0.1:$port/jsonrpc", "websocket", pwd); - state = state.copyWith(aria2c: aria2c); - aria2c.getVersion().then((value) { - dPrint("Aria2cManager.connected! version == ${value.version}"); - _listenState(aria2c); - }); - final box = await Hive.openBox("app_conf"); - aria2c.changeGlobalOption(Aria2Option() - ..maxOverallUploadLimit = - textToByte(box.get("downloader_up_limit", defaultValue: "0")) - ..maxOverallDownloadLimit = - textToByte(box.get("downloader_down_limit", defaultValue: "0")) - ..btTracker = trackerList); - } + // Future _onLaunch(int port, String pwd, String trackerList) async { + // final aria2c = Aria2c("ws://127.0.0.1:$port/jsonrpc", "websocket", pwd); + // state = state.copyWith(aria2c: aria2c); + // aria2c.getVersion().then((value) { + // dPrint("Aria2cManager.connected! version == ${value.version}"); + // _listenState(aria2c); + // }); + // final box = await Hive.openBox("app_conf"); + // aria2c.changeGlobalOption( + // Aria2Option() + // ..maxOverallUploadLimit = textToByte(box.get("downloader_up_limit", defaultValue: "0")) + // ..maxOverallDownloadLimit = textToByte(box.get("downloader_down_limit", defaultValue: "0")) + // ..btTracker = trackerList, + // ); + // } - Future _listenState(Aria2c aria2c) async { - dPrint("Aria2cModel._listenState start"); - while (true) { - if (_disposed || state.aria2c == null) { - dPrint("Aria2cModel._listenState end"); - return; - } - try { - final aria2globalStat = await aria2c.getGlobalStat(); - state = state.copyWith(aria2globalStat: aria2globalStat); - } catch (e) { - dPrint("aria2globalStat update error:$e"); - } - await Future.delayed(const Duration(seconds: 1)); - } - } + // Future _listenState(Aria2c aria2c) async { + // dPrint("Aria2cModel._listenState start"); + // while (true) { + // if (_disposed || state.aria2c == null) { + // dPrint("Aria2cModel._listenState end"); + // return; + // } + // try { + // final aria2globalStat = await aria2c.getGlobalStat(); + // state = state.copyWith(aria2globalStat: aria2globalStat); + // } catch (e) { + // dPrint("aria2globalStat update error:$e"); + // } + // await Future.delayed(const Duration(seconds: 1)); + // } + // } + // } } diff --git a/lib/provider/aria2c.g.dart b/lib/provider/aria2c.g.dart index c3877b2..a6543cb 100644 --- a/lib/provider/aria2c.g.dart +++ b/lib/provider/aria2c.g.dart @@ -41,7 +41,7 @@ final class Aria2cModelProvider } } -String _$aria2cModelHash() => r'3d51aeefd92e5291dca1f01db961f9c5496ec24f'; +String _$aria2cModelHash() => r'6fe18708151958dc19a01782f053f3f581fd7913'; abstract class _$Aria2cModel extends $Notifier { Aria2cModelState build(); diff --git a/lib/provider/unp4kc.dart b/lib/provider/unp4kc.dart index c5c2236..da61ef9 100644 --- a/lib/provider/unp4kc.dart +++ b/lib/provider/unp4kc.dart @@ -9,13 +9,10 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/common/conf/binary_conf.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; -import 'package:starcitizen_doctor/common/rust/api/rs_process.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/provider.dart'; import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart'; import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart'; -import 'package:starcitizen_doctor/common/rust/api/rs_process.dart' - as rs_process; part 'unp4kc.freezed.dart'; @@ -40,10 +37,7 @@ class Unp4kCModel extends _$Unp4kCModel { @override Unp4kcState build() { - state = Unp4kcState( - startUp: false, - curPath: '\\', - endMessage: S.current.tools_unp4k_msg_init); + state = Unp4kcState(startUp: false, curPath: '\\', endMessage: S.current.tools_unp4k_msg_init); _init(); return state; } @@ -52,48 +46,45 @@ class Unp4kCModel extends _$Unp4kCModel { String getGamePath() => _toolsState.scInstalledPath; - bool _hasUnp4kRunTimeError = false; + final bool _hasUnp4kRunTimeError = false; void _init() async { final execDir = "${appGlobalState.applicationBinaryModuleDir}\\unp4kc"; - await BinaryModuleConf.extractModule( - ["unp4kc"], appGlobalState.applicationBinaryModuleDir!); + await BinaryModuleConf.extractModule(["unp4kc"], appGlobalState.applicationBinaryModuleDir!); final exec = "$execDir\\unp4kc.exe"; - final stream = rs_process.start( - executable: exec, arguments: [], workingDirectory: execDir); + // final stream = rs_process.start(executable: exec, arguments: [], workingDirectory: execDir); - stream.listen((event) async { - switch (event.dataType) { - case RsProcessStreamDataType.output: - _rsPid = event.rsPid; - try { - final eventJson = await compute(json.decode, event.data); - _handleMessage(eventJson, event.rsPid); - } catch (e) { - dPrint("[unp4kc] json error: $e"); - } - break; - case RsProcessStreamDataType.error: - dPrint("[unp4kc] stderr: ${event.data}"); - if (state.errorMessage.isEmpty) { - state = state.copyWith(errorMessage: event.data); - } else { - state = state.copyWith( - errorMessage: "${state.errorMessage}\n${event.data}"); - } - if (!_hasUnp4kRunTimeError) { - if (checkRunTimeError(state.errorMessage)) { - _hasUnp4kRunTimeError = true; - AnalyticsApi.touch("unp4k_no_runtime"); - } - } - break; - case RsProcessStreamDataType.exit: - dPrint("[unp4kc] exit: ${event.data}"); - break; - } - }); + // stream.listen((event) async { + // switch (event.dataType) { + // case RsProcessStreamDataType.output: + // _rsPid = event.rsPid; + // try { + // final eventJson = await compute(json.decode, event.data); + // _handleMessage(eventJson, event.rsPid); + // } catch (e) { + // dPrint("[unp4kc] json error: $e"); + // } + // break; + // case RsProcessStreamDataType.error: + // dPrint("[unp4kc] stderr: ${event.data}"); + // if (state.errorMessage.isEmpty) { + // state = state.copyWith(errorMessage: event.data); + // } else { + // state = state.copyWith(errorMessage: "${state.errorMessage}\n${event.data}"); + // } + // if (!_hasUnp4kRunTimeError) { + // if (checkRunTimeError(state.errorMessage)) { + // _hasUnp4kRunTimeError = true; + // AnalyticsApi.touch("unp4k_no_runtime"); + // } + // } + // break; + // case RsProcessStreamDataType.exit: + // dPrint("[unp4kc] exit: ${event.data}"); + // break; + // } + // }); ref.onDispose(() { state = state.copyWith(fs: null); @@ -113,7 +104,7 @@ class Unp4kCModel extends _$Unp4kCModel { final gameP4kPath = "$gamePath\\Data.p4k"; switch (action.toString().trim()) { case "info: startup": - rs_process.write(rsPid: rsPid, data: "$gameP4kPath\n"); + // rs_process.write(rsPid: rsPid, data: "$gameP4kPath\n"); break; case "info: Reading_p4k_file": _loadStartTime = DateTime.now(); @@ -132,23 +123,22 @@ class Unp4kCModel extends _$Unp4kCModel { final item = AppUnp4kP4kItemData.fromJson(p4kFiles[i]); item.name = "${item.name}"; files["\\${item.name}"] = item; - await fs - .file(item.name?.replaceAll("\\", "/") ?? "") - .create(recursive: true); + await fs.file(item.name?.replaceAll("\\", "/") ?? "").create(recursive: true); if (i == nextAwait) { - state = state.copyWith( - endMessage: - S.current.tools_unp4k_msg_reading3(i, p4kFiles.length)); + state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading3(i, p4kFiles.length)); await Future.delayed(Duration.zero); nextAwait += 20000; } } final endTime = DateTime.now(); state = state.copyWith( - files: files, - fs: fs, - endMessage: S.current.tools_unp4k_msg_read_completed(files.length, - endTime.difference(_loadStartTime!).inMilliseconds)); + files: files, + fs: fs, + endMessage: S.current.tools_unp4k_msg_read_completed( + files.length, + endTime.difference(_loadStartTime!).inMilliseconds, + ), + ); _loadStartTime = null; break; case "info: Extracted_Open": @@ -168,8 +158,9 @@ class Unp4kCModel extends _$Unp4kCModel { } } state = state.copyWith( - tempOpenFile: MapEntry(openType, filePath), - endMessage: S.current.tools_unp4k_msg_open_file(filePath)); + tempOpenFile: MapEntry(openType, filePath), + endMessage: S.current.tools_unp4k_msg_open_file(filePath), + ); break; default: dPrint("[unp4kc] unknown action: $action"); @@ -204,8 +195,7 @@ class Unp4kCModel extends _$Unp4kCModel { result.add(f); } } else { - result.add(AppUnp4kP4kItemData( - name: file.path.replaceAll("/", "\\"), isDirectory: true)); + result.add(AppUnp4kP4kItemData(name: file.path.replaceAll("/", "\\"), isDirectory: true)); } } return result; @@ -221,16 +211,15 @@ class Unp4kCModel extends _$Unp4kCModel { Future openFile(String filePath) async { final tempDir = await getTemporaryDirectory(); - final tempPath = - "${tempDir.absolute.path}\\SCToolbox_unp4kc\\${SCLoggerHelper.getGameChannelID(getGamePath())}\\"; + final tempPath = "${tempDir.absolute.path}\\SCToolbox_unp4kc\\${SCLoggerHelper.getGameChannelID(getGamePath())}\\"; state = state.copyWith( - tempOpenFile: const MapEntry("loading", ""), - endMessage: S.current.tools_unp4k_msg_open_file(filePath)); + tempOpenFile: const MapEntry("loading", ""), + endMessage: S.current.tools_unp4k_msg_open_file(filePath), + ); extractFile(filePath, tempPath, mode: "extract_open"); } - Future extractFile(String filePath, String outputPath, - {String mode = "extract"}) async { + Future extractFile(String filePath, String outputPath, {String mode = "extract"}) async { // remove first \\ if (filePath.startsWith("\\")) { filePath = filePath.substring(1); @@ -238,35 +227,28 @@ class Unp4kCModel extends _$Unp4kCModel { outputPath = "$outputPath$filePath"; dPrint("extractFile .... $filePath"); if (_rsPid != null) { - rs_process.write( - rsPid: _rsPid!, data: "$mode<:,:>$filePath<:,:>$outputPath\n"); + // rs_process.write(rsPid: _rsPid!, data: "$mode<:,:>$filePath<:,:>$outputPath\n"); } } static bool checkRunTimeError(String errorMessage) { - if (errorMessage - .contains("You must install .NET to run this application") || - errorMessage.contains( - "You must install or update .NET to run this application") || - errorMessage.contains( - "It was not possible to find any compatible framework version")) { + if (errorMessage.contains("You must install .NET to run this application") || + errorMessage.contains("You must install or update .NET to run this application") || + errorMessage.contains("It was not possible to find any compatible framework version")) { AnalyticsApi.touch("unp4k_no_runtime"); return true; } return false; } - static Future unp4kTools( - String applicationBinaryModuleDir, List args) async { - await BinaryModuleConf.extractModule( - ["unp4kc"], applicationBinaryModuleDir); + static Future unp4kTools(String applicationBinaryModuleDir, List args) async { + await BinaryModuleConf.extractModule(["unp4kc"], applicationBinaryModuleDir); final execDir = "$applicationBinaryModuleDir\\unp4kc"; final exec = "$execDir\\unp4kc.exe"; final r = await Process.run(exec, args); if (r.exitCode != 0) { Process.killPid(r.pid); - throw Exception( - "error: ${r.exitCode} , info= ${r.stdout} , err= ${r.stderr}"); + throw Exception("error: ${r.exitCode} , info= ${r.stdout} , err= ${r.stderr}"); } final eventJson = await compute(json.decode, r.stdout.toString()); if (eventJson["action"] == "data: Uint8List") { diff --git a/lib/provider/unp4kc.g.dart b/lib/provider/unp4kc.g.dart index 5b04a8a..34bcbda 100644 --- a/lib/provider/unp4kc.g.dart +++ b/lib/provider/unp4kc.g.dart @@ -41,7 +41,7 @@ final class Unp4kCModelProvider } } -String _$unp4kCModelHash() => r'410461980f6173fdbb5d92cbaa3f4c2f57c1ad8d'; +String _$unp4kCModelHash() => r'5ea79d8084a79353742a3344102fb5725b50211b'; abstract class _$Unp4kCModel extends $Notifier { Unp4kcState build(); diff --git a/lib/ui/about/about_ui.dart b/lib/ui/about/about_ui.dart index 9cffb22..b3bf28f 100644 --- a/lib/ui/about/about_ui.dart +++ b/lib/ui/about/about_ui.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -45,7 +46,7 @@ class AboutUI extends HookConsumerWidget { const SizedBox(height: 12), Button( onPressed: () => _onCheckUpdate(context, ref), - child: Padding(padding: const EdgeInsets.all(4), child: Text(S.current.about_check_update)), + child: Padding(padding: const EdgeInsets.all(4), child: Text("获取完整版")), ), const SizedBox(height: 32), Container( @@ -598,8 +599,10 @@ class AboutUI extends HookConsumerWidget { } Future _onCheckUpdate(BuildContext context, WidgetRef ref) async { - if (ConstConf.isMSE) { - launchUrlString("ms-windows-store://pdp/?productid=9NF3SWFWNKL1"); + if (ConstConf.isMSE || kIsWeb) { + launchUrlString( + "https://citizenwiki.cn/Localization#%E6%96%B9%E5%BC%8F2%EF%BC%9ASC%E6%B1%89%E5%8C%96%E7%9B%92%E5%AD%90%E4%B8%80%E9%94%AE%E6%B1%89%E5%8C%96:~:text=%E6%97%A0%E6%B3%95%E8%BE%93%E5%85%A5%E4%B8%AD%E6%96%87-,%E6%96%B9%E5%BC%8F2%EF%BC%9ASC%E6%B1%89%E5%8C%96%E7%9B%92%E5%AD%90%E4%B8%80%E9%94%AE%E6%B1%89%E5%8C%96,-SC%E6%B1%89%E5%8C%96%E7%9B%92%E5%AD%90", + ); return; } else { final hasUpdate = await ref.read(appGlobalModelProvider.notifier).checkUpdate(context); diff --git a/lib/ui/home/game_doctor/game_doctor_ui.dart b/lib/ui/home/game_doctor/game_doctor_ui.dart index 3b88bfd..4801994 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui.dart @@ -1,8 +1,12 @@ +import 'dart:io'; + import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_tilt/flutter_tilt.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; @@ -26,20 +30,53 @@ class HomeGameDoctorUI extends HookConsumerWidget { AnalyticsApi.touch("auto_scan_issues"); SchedulerBinding.instance.addPostFrameCallback((timeStamp) { dPrint("HomeGameDoctorUI useEffect doCheck timeStamp === $timeStamp"); - model.doCheck(context); + if (kIsWeb) { + // Web 平台自动弹出文件选择器 + _selectLogFile(context, model); + } else { + model.doCheck(context); + } }); return null; }, []); - return makeDefaultPage(context, - title: S.current - .doctor_title_one_click_diagnosis(homeState.scInstalledPath ?? ""), - useBodyContainer: true, - content: Stack( - children: [ - Column( - children: [ - const SizedBox(height: 12), + return makeDefaultPage( + context, + title: S.current.doctor_title_one_click_diagnosis(homeState.scInstalledPath ?? ""), + useBodyContainer: true, + content: Stack( + children: [ + Column( + children: [ + const SizedBox(height: 12), + if (kIsWeb) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + FilledButton( + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + const Icon(FluentIcons.folder_open), + const SizedBox(width: 6), + Text(S.current.doctor_action_select_log_file), + ], + ), + ), + onPressed: () => _selectLogFile(context, model), + ), + if (state.customLogFilePath != null) ...[ + const SizedBox(width: 12), + Expanded( + child: Text(S.current.doctor_info_log_file_selected, style: TextStyle(color: Colors.green)), + ), + ], + ], + ), + ) + else Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -50,118 +87,102 @@ class HomeGameDoctorUI extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(left: 6, right: 6), child: Button( - child: Padding( - padding: const EdgeInsets.all(4), - child: Row( - children: [ - const Icon(FluentIcons.folder_open), - const SizedBox(width: 6), - Text(item.value), - ], - ), + child: Padding( + padding: const EdgeInsets.all(4), + child: Row( + children: [ + const Icon(FluentIcons.folder_open), + const SizedBox(width: 6), + Text(item.value), + ], ), - onPressed: () => - _onTapButton(context, item.key, homeState)), + ), + onPressed: () => _onTapButton(context, item.key, homeState), + ), ), ], ), - if (state.isChecking) - Expanded( - child: Center( + if (state.isChecking) + Expanded( + child: Center( child: Column( mainAxisSize: MainAxisSize.min, - children: [ - const ProgressRing(), - const SizedBox(height: 12), - Text(state.lastScreenInfo) - ], + children: [const ProgressRing(), const SizedBox(height: 12), Text(state.lastScreenInfo)], ), - )) - else if (state.checkResult == null || - state.checkResult!.isEmpty) ...[ - Expanded( - child: Center( + ), + ) + else if (state.checkResult == null || state.checkResult!.isEmpty) ...[ + Expanded( + child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 12), - Text(S.current.doctor_info_scan_complete_no_issues, - maxLines: 1), + Text(S.current.doctor_info_scan_complete_no_issues, maxLines: 1), const SizedBox(height: 64), ], ), - )) - ] else - ...makeResult(context, state, model), - ], - ), - if (state.isFixing) - Container( - decoration: BoxDecoration( - color: Colors.black.withAlpha(150), - ), - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const ProgressRing(), - const SizedBox(height: 12), - Text(state.isFixingString.isNotEmpty - ? state.isFixingString - : S.current.doctor_info_processing), - ], ), ), + ] else + ...makeResult(context, state, model), + ], + ), + if (state.isFixing) + Container( + decoration: BoxDecoration(color: Colors.black.withAlpha(150)), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const ProgressRing(), + const SizedBox(height: 12), + Text(state.isFixingString.isNotEmpty ? state.isFixingString : S.current.doctor_info_processing), + ], + ), ), - Positioned( - bottom: 20, - right: 20, - child: makeRescueBanner(context), - ) - ], - )); + ), + Positioned(bottom: 20, right: 20, child: makeRescueBanner(context)), + ], + ), + ); } Widget makeRescueBanner(BuildContext context) { return GestureDetector( onTap: () async { - await showToast( - context, S.current.doctor_info_game_rescue_service_note); + await showToast(context, S.current.doctor_info_game_rescue_service_note); launchUrlString( - "https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=-M4wEme_bCXbUGT4LFKLH0bAYTFt70Ad&authKey=vHVr0TNgRmKu%2BHwywoJV6EiLa7La2VX74Vkyixr05KA0H9TqB6qWlCdY%2B9jLQ4Ha&noverify=0&group_code=536454632"); + "https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=-M4wEme_bCXbUGT4LFKLH0bAYTFt70Ad&authKey=vHVr0TNgRmKu%2BHwywoJV6EiLa7La2VX74Vkyixr05KA0H9TqB6qWlCdY%2B9jLQ4Ha&noverify=0&group_code=536454632", + ); }, child: Tilt( shadowConfig: const ShadowConfig(maxIntensity: .2), borderRadius: BorderRadius.circular(12), child: Container( - decoration: BoxDecoration( - color: FluentTheme.of(context).cardColor, + decoration: BoxDecoration(color: FluentTheme.of(context).cardColor), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset("assets/rescue.png", width: 24, height: 24), + const SizedBox(width: 12), + Text(S.current.doctor_info_need_help), + ], ), - child: Padding( - padding: const EdgeInsets.all(12), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset("assets/rescue.png", width: 24, height: 24), - const SizedBox(width: 12), - Text(S.current.doctor_info_need_help), - ], - ), - )), + ), + ), ), ); } - List makeResult(BuildContext context, HomeGameDoctorState state, - HomeGameDoctorUIModel model) { + List makeResult(BuildContext context, HomeGameDoctorState state, HomeGameDoctorUIModel model) { return [ const SizedBox(height: 24), Text(state.lastScreenInfo, maxLines: 1), const SizedBox(height: 12), - Text( - S.current.doctor_info_tool_check_result_note, - style: TextStyle(color: Colors.red, fontSize: 16), - ), + Text(S.current.doctor_info_tool_check_result_note, style: TextStyle(color: Colors.red, fontSize: 16)), const SizedBox(height: 24), ListView.builder( itemCount: state.checkResult!.length, @@ -176,43 +197,54 @@ class HomeGameDoctorUI extends HookConsumerWidget { ]; } - Widget makeResultItem(BuildContext context, MapEntry item, - HomeGameDoctorState state, HomeGameDoctorUIModel model) { + Widget makeResultItem( + BuildContext context, + MapEntry item, + HomeGameDoctorState state, + HomeGameDoctorUIModel model, + ) { final errorNames = { - "unSupport_system": MapEntry(S.current.doctor_info_result_unsupported_os, - S.current.doctor_info_result_upgrade_system(item.value)), - "no_live_path": MapEntry(S.current.doctor_info_result_missing_live_folder, - S.current.doctor_info_result_create_live_folder(item.value)), + "unSupport_system": MapEntry( + S.current.doctor_info_result_unsupported_os, + S.current.doctor_info_result_upgrade_system(item.value), + ), + "no_live_path": MapEntry( + S.current.doctor_info_result_missing_live_folder, + S.current.doctor_info_result_create_live_folder(item.value), + ), "nvme_PhysicalBytes": MapEntry( - S.current.doctor_info_result_incompatible_nvme_device, - S.current.doctor_info_result_add_registry_value(item.value)), + S.current.doctor_info_result_incompatible_nvme_device, + S.current.doctor_info_result_add_registry_value(item.value), + ), "eac_file_miss": MapEntry( - S.current.doctor_info_result_missing_easyanticheat_files, - S.current.doctor_info_result_verify_files_with_rsi_launcher), + S.current.doctor_info_result_missing_easyanticheat_files, + S.current.doctor_info_result_verify_files_with_rsi_launcher, + ), "eac_not_install": MapEntry( - S.current.doctor_info_result_easyanticheat_not_installed, - S.current.doctor_info_result_install_easyanticheat), - "cn_user_name": MapEntry(S.current.doctor_info_result_chinese_username, - S.current.doctor_info_result_chinese_username_error), + S.current.doctor_info_result_easyanticheat_not_installed, + S.current.doctor_info_result_install_easyanticheat, + ), + "cn_user_name": MapEntry( + S.current.doctor_info_result_chinese_username, + S.current.doctor_info_result_chinese_username_error, + ), "cn_install_path": MapEntry( - S.current.doctor_info_result_chinese_install_path, - S.current.doctor_info_result_chinese_install_path_error(item.value)), - "low_ram": MapEntry(S.current.doctor_info_result_low_physical_memory, - S.current.doctor_info_result_memory_requirement(item.value)), + S.current.doctor_info_result_chinese_install_path, + S.current.doctor_info_result_chinese_install_path_error(item.value), + ), + "low_ram": MapEntry( + S.current.doctor_info_result_low_physical_memory, + S.current.doctor_info_result_memory_requirement(item.value), + ), }; bool isCheckedError = errorNames.containsKey(item.key); if (isCheckedError) { return Container( - decoration: BoxDecoration( - color: FluentTheme.of(context).cardColor, - ), + decoration: BoxDecoration(color: FluentTheme.of(context).cardColor), margin: const EdgeInsets.only(bottom: 12), child: ListTile( - title: Text( - errorNames[item.key]?.key ?? "", - style: const TextStyle(fontSize: 18), - ), + title: Text(errorNames[item.key]?.key ?? "", style: const TextStyle(fontSize: 18)), subtitle: Padding( padding: const EdgeInsets.only(top: 4, bottom: 4), child: Column( @@ -220,10 +252,9 @@ class HomeGameDoctorUI extends HookConsumerWidget { const SizedBox(height: 4), Text( S.current.doctor_info_result_fix_suggestion( - errorNames[item.key]?.value ?? - S.current.doctor_info_result_no_solution), - style: TextStyle( - fontSize: 14, color: Colors.white.withValues(alpha: .7)), + errorNames[item.key]?.value ?? S.current.doctor_info_result_no_solution, + ), + style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .7)), ), ], ), @@ -235,8 +266,7 @@ class HomeGameDoctorUI extends HookConsumerWidget { await model.doFix(context, item); }, child: Padding( - padding: - const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4), + padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4), child: Text(S.current.doctor_info_action_fix), ), ), @@ -247,26 +277,16 @@ class HomeGameDoctorUI extends HookConsumerWidget { final isSubTitleUrl = item.value.startsWith("https://"); return Container( - decoration: BoxDecoration( - color: FluentTheme.of(context).cardColor, - ), + decoration: BoxDecoration(color: FluentTheme.of(context).cardColor), margin: const EdgeInsets.only(bottom: 12), child: ListTile( - title: Text( - item.key, - style: const TextStyle(fontSize: 18), - ), + title: Text(item.key, style: const TextStyle(fontSize: 18)), subtitle: isSubTitleUrl ? null : Column( children: [ const SizedBox(height: 4), - Text( - item.value, - style: TextStyle( - fontSize: 14, - color: Colors.white.withValues(alpha: .7)), - ), + Text(item.value, style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .7))), ], ), trailing: isSubTitleUrl @@ -275,8 +295,7 @@ class HomeGameDoctorUI extends HookConsumerWidget { launchUrlString(item.value); }, child: Padding( - padding: const EdgeInsets.only( - left: 8, right: 8, top: 4, bottom: 4), + padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4), child: Text(S.current.doctor_action_view_solution), ), ) @@ -285,8 +304,7 @@ class HomeGameDoctorUI extends HookConsumerWidget { ); } - Future _onTapButton( - BuildContext context, String key, HomeUIModelState homeState) async { + Future _onTapButton(BuildContext context, String key, HomeUIModelState homeState) async { switch (key) { case "rsi_log": final path = await SCLoggerHelper.getLogFilePath(); @@ -294,8 +312,7 @@ class HomeGameDoctorUI extends HookConsumerWidget { SystemHelper.openDir(path); return; case "game_log": - if (homeState.scInstalledPath == "not_install" || - homeState.scInstalledPath == null) { + if (homeState.scInstalledPath == "not_install" || homeState.scInstalledPath == null) { showToast(context, S.current.doctor_tip_title_select_game_directory); return; } @@ -303,4 +320,37 @@ class HomeGameDoctorUI extends HookConsumerWidget { return; } } + + Future _selectLogFile(BuildContext context, HomeGameDoctorUIModel model) async { + try { + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['log'], + dialogTitle: S.current.doctor_action_select_log_file, + ); + + if (result != null && result.files.isNotEmpty) { + final file = result.files.first; + + // 在 web 平台,使用 bytes 读取文件内容 + if (kIsWeb && file.bytes != null) { + final content = String.fromCharCodes(file.bytes!); + final lines = content.split('\n'); + model.setCustomLogFile(file.name, lines); + if (!context.mounted) return; + // Web 平台选择文件后自动开始诊断 + model.doCheck(context); + } else if (!kIsWeb && file.path != null) { + // 桌面平台使用路径读取 + if (!kIsWeb) { + final logFile = File(file.path!); + final lines = await logFile.readAsLines(); + model.setCustomLogFile(file.path!, lines); + } + } + } + } catch (e) { + dPrint("Error selecting log file: $e"); + } + } } diff --git a/lib/ui/home/game_doctor/game_doctor_ui_model.dart b/lib/ui/home/game_doctor/game_doctor_ui_model.dart index 45e6580..5f608f5 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui_model.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui_model.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; @@ -23,6 +24,8 @@ abstract class HomeGameDoctorState with _$HomeGameDoctorState { @Default("") String lastScreenInfo, @Default("") String isFixingString, List>? checkResult, + String? customLogFilePath, + List? customLogFileContent, }) = _HomeGameDoctorState; } @@ -34,12 +37,16 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { return state; } + void setCustomLogFile(String? filePath, List? content) { + state = state.copyWith(customLogFilePath: filePath, customLogFileContent: content); + } + Future doFix( - // ignore: avoid_build_context_in_providers - BuildContext context, - MapEntry item) async { - final checkResult = - List>.from(state.checkResult ?? []); + // ignore: avoid_build_context_in_providers + BuildContext context, + MapEntry item, + ) async { + final checkResult = List>.from(state.checkResult ?? []); state = state.copyWith(isFixing: true, isFixingString: ""); switch (item.key) { case "unSupport_system": @@ -49,13 +56,11 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { try { await Directory(item.value).create(recursive: true); if (!context.mounted) break; - showToast( - context, S.current.doctor_action_result_create_folder_success); + showToast(context, S.current.doctor_action_result_create_folder_success); checkResult.remove(item); state = state.copyWith(checkResult: checkResult); } catch (e) { - showToast(context, - S.current.doctor_action_result_create_folder_fail(item.value, e)); + showToast(context, S.current.doctor_action_result_create_folder_fail(item.value, e)); } break; case "nvme_PhysicalBytes": @@ -71,8 +76,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { } break; case "eac_file_miss": - showToast(context, - S.current.doctor_info_result_verify_files_with_rsi_launcher); + showToast(context, S.current.doctor_info_result_verify_files_with_rsi_launcher); break; case "eac_not_install": final eacJsonPath = "${item.value}\\Settings.json"; @@ -80,19 +84,16 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final Map eacJson = json.decode(utf8.decode(eacJsonData)); final eacID = eacJson["productid"]; try { - var result = await Process.run( - "${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]); + var result = await Process.run("${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]); dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID"); if (result.stderr == "") { if (!context.mounted) break; - showToast( - context, S.current.doctor_action_result_game_start_success); + showToast(context, S.current.doctor_action_result_game_start_success); checkResult.remove(item); state = state.copyWith(checkResult: checkResult); } else { if (!context.mounted) break; - showToast(context, - S.current.doctor_action_result_fix_fail(result.stderr)); + showToast(context, S.current.doctor_action_result_fix_fail(result.stderr)); } } catch (e) { if (!context.mounted) break; @@ -102,8 +103,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { case "cn_user_name": showToast(context, S.current.doctor_action_result_redirect_warning); await Future.delayed(const Duration(milliseconds: 300)); - launchUrlString( - "https://jingyan.baidu.com/article/59703552a318a08fc0074021.html"); + launchUrlString("https://jingyan.baidu.com/article/59703552a318a08fc0074021.html"); break; default: showToast(context, S.current.doctor_action_result_issue_not_supported); @@ -115,8 +115,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { // ignore: avoid_build_context_in_providers Future doCheck(BuildContext context) async { if (state.isChecking) return; - state = state.copyWith( - isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing); + state = state.copyWith(isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing); dPrint("-------- start docker check -----"); if (!context.mounted) return; await _statCheck(context); @@ -134,62 +133,73 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { // checkResult?.add(MapEntry("nvme_PhysicalBytes", "C")); // checkResult?.add(MapEntry("no_live_path", "")); - await _checkPreInstall(context, scInstalledPath, checkResult); - if (!context.mounted) return; - await _checkEAC(context, scInstalledPath, checkResult); - if (!context.mounted) return; + // Web 平台仅检查日志文件,跳过系统环境和 EAC 检查 + if (!kIsWeb) { + await _checkPreInstall(context, scInstalledPath, checkResult); + if (!context.mounted) return; + await _checkEAC(context, scInstalledPath, checkResult); + if (!context.mounted) return; + } await _checkGameRunningLog(context, scInstalledPath, checkResult); if (checkResult.isEmpty) { final lastScreenInfo = S.current.doctor_action_result_analysis_no_issue; state = state.copyWith(checkResult: null, lastScreenInfo: lastScreenInfo); } else { - final lastScreenInfo = S.current - .doctor_action_result_analysis_issues_found( - checkResult.length.toString()); - state = state.copyWith( - checkResult: checkResult, lastScreenInfo: lastScreenInfo); + final lastScreenInfo = S.current.doctor_action_result_analysis_issues_found(checkResult.length.toString()); + state = state.copyWith(checkResult: checkResult, lastScreenInfo: lastScreenInfo); } - if (scInstalledPath == "not_install" && (checkResult.isEmpty)) { + // Web 平台不显示 not_install 提示 + if (!kIsWeb && scInstalledPath == "not_install" && (checkResult.isEmpty)) { if (!context.mounted) return; showToast(context, S.current.doctor_action_result_toast_scan_no_issue); } } // ignore: avoid_build_context_in_providers - Future _checkGameRunningLog(BuildContext context, String scInstalledPath, - List> checkResult) async { - if (scInstalledPath == "not_install") return; + Future _checkGameRunningLog( + BuildContext context, + String scInstalledPath, + List> checkResult, + ) async { + if (scInstalledPath == "not_install" && state.customLogFileContent == null) return; final lastScreenInfo = S.current.doctor_action_tip_checking_game_log; state = state.copyWith(lastScreenInfo: lastScreenInfo); - final logs = await SCLoggerHelper.getGameRunningLogs(scInstalledPath); + + // 优先使用自定义 log 文件内容(用于 web 平台) + List? logs; + if (state.customLogFileContent != null) { + logs = state.customLogFileContent; + } else { + logs = await SCLoggerHelper.getGameRunningLogs(scInstalledPath); + } + if (logs == null) return; final info = SCLoggerHelper.getGameRunningLogInfo(logs); if (info != null) { if (info.key != "_") { - checkResult.add(MapEntry( - S.current.doctor_action_info_game_abnormal_exit(info.key), - info.value)); + checkResult.add(MapEntry(S.current.doctor_action_info_game_abnormal_exit(info.key), info.value)); } else { - checkResult.add(MapEntry( + checkResult.add( + MapEntry( S.current.doctor_action_info_game_abnormal_exit_unknown, - S.current.doctor_action_info_info_feedback(info.value))); + S.current.doctor_action_info_info_feedback(info.value), + ), + ); } } } // ignore: avoid_build_context_in_providers - Future _checkEAC(BuildContext context, String scInstalledPath, - List> checkResult) async { + Future _checkEAC(BuildContext context, String scInstalledPath, List> checkResult) async { if (scInstalledPath == "not_install") return; final lastScreenInfo = S.current.doctor_action_info_checking_eac; state = state.copyWith(lastScreenInfo: lastScreenInfo); final eacPath = "$scInstalledPath\\EasyAntiCheat"; final eacJsonPath = "$eacPath\\Settings.json"; - if (!await Directory(eacPath).exists() || - !await File(eacJsonPath).exists()) { + if (!await Directory(eacPath).exists() || !await File(eacJsonPath).exists()) { checkResult.add(const MapEntry("eac_file_miss", "")); return; } @@ -212,17 +222,18 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final _cnExp = RegExp(r"[^\x00-\xff]"); // ignore: avoid_build_context_in_providers - Future _checkPreInstall(BuildContext context, String scInstalledPath, - List> checkResult) async { + Future _checkPreInstall( + BuildContext context, + String scInstalledPath, + List> checkResult, + ) async { final lastScreenInfo = S.current.doctor_action_info_checking_runtime; state = state.copyWith(lastScreenInfo: lastScreenInfo); if (!(Platform.operatingSystemVersion.contains("Windows 10") || Platform.operatingSystemVersion.contains("Windows 11"))) { - checkResult - .add(MapEntry("unSupport_system", Platform.operatingSystemVersion)); - final lastScreenInfo = S.current.doctor_action_result_info_unsupported_os( - Platform.operatingSystemVersion); + checkResult.add(MapEntry("unSupport_system", Platform.operatingSystemVersion)); + final lastScreenInfo = S.current.doctor_action_result_info_unsupported_os(Platform.operatingSystemVersion); state = state.copyWith(lastScreenInfo: lastScreenInfo); await showToast(context, lastScreenInfo); } @@ -236,12 +247,10 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { if (ramSize < 16) { checkResult.add(MapEntry("low_ram", "$ramSize")); } - state = state.copyWith( - lastScreenInfo: S.current.doctor_action_info_checking_install_info); + state = state.copyWith(lastScreenInfo: S.current.doctor_action_info_checking_install_info); // 检查安装分区 try { - final listData = await SCLoggerHelper.getGameInstallPath( - await SCLoggerHelper.getLauncherLogList() ?? []); + final listData = await SCLoggerHelper.getGameInstallPath(await SCLoggerHelper.getLauncherLogList() ?? []); final p = []; final checkedPath = []; for (var installPath in listData) { @@ -265,10 +274,9 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { // call check for (var element in p) { var result = await Process.run('powershell', [ - "(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()" + "(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()", ]); - dPrint( - "fsutil info sector info: ->>> ${result.stdout.toString().trim()}"); + dPrint("fsutil info sector info: ->>> ${result.stdout.toString().trim()}"); if (result.stderr == "") { final rs = result.stdout.toString().trim(); final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0); diff --git a/lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart b/lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart index 2217959..ce15f49 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$HomeGameDoctorState { - bool get isChecking; bool get isFixing; String get lastScreenInfo; String get isFixingString; List>? get checkResult; + bool get isChecking; bool get isFixing; String get lastScreenInfo; String get isFixingString; List>? get checkResult; String? get customLogFilePath; List? get customLogFileContent; /// Create a copy of HomeGameDoctorState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $HomeGameDoctorStateCopyWith get copyWith => _$HomeGameDoct @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeGameDoctorState&&(identical(other.isChecking, isChecking) || other.isChecking == isChecking)&&(identical(other.isFixing, isFixing) || other.isFixing == isFixing)&&(identical(other.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.isFixingString, isFixingString) || other.isFixingString == isFixingString)&&const DeepCollectionEquality().equals(other.checkResult, checkResult)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeGameDoctorState&&(identical(other.isChecking, isChecking) || other.isChecking == isChecking)&&(identical(other.isFixing, isFixing) || other.isFixing == isFixing)&&(identical(other.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.isFixingString, isFixingString) || other.isFixingString == isFixingString)&&const DeepCollectionEquality().equals(other.checkResult, checkResult)&&(identical(other.customLogFilePath, customLogFilePath) || other.customLogFilePath == customLogFilePath)&&const DeepCollectionEquality().equals(other.customLogFileContent, customLogFileContent)); } @override -int get hashCode => Object.hash(runtimeType,isChecking,isFixing,lastScreenInfo,isFixingString,const DeepCollectionEquality().hash(checkResult)); +int get hashCode => Object.hash(runtimeType,isChecking,isFixing,lastScreenInfo,isFixingString,const DeepCollectionEquality().hash(checkResult),customLogFilePath,const DeepCollectionEquality().hash(customLogFileContent)); @override String toString() { - return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult)'; + return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult, customLogFilePath: $customLogFilePath, customLogFileContent: $customLogFileContent)'; } @@ -45,7 +45,7 @@ abstract mixin class $HomeGameDoctorStateCopyWith<$Res> { factory $HomeGameDoctorStateCopyWith(HomeGameDoctorState value, $Res Function(HomeGameDoctorState) _then) = _$HomeGameDoctorStateCopyWithImpl; @useResult $Res call({ - bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult + bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult, String? customLogFilePath, List? customLogFileContent }); @@ -62,14 +62,16 @@ class _$HomeGameDoctorStateCopyWithImpl<$Res> /// Create a copy of HomeGameDoctorState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? isChecking = null,Object? isFixing = null,Object? lastScreenInfo = null,Object? isFixingString = null,Object? checkResult = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? isChecking = null,Object? isFixing = null,Object? lastScreenInfo = null,Object? isFixingString = null,Object? checkResult = freezed,Object? customLogFilePath = freezed,Object? customLogFileContent = freezed,}) { return _then(_self.copyWith( isChecking: null == isChecking ? _self.isChecking : isChecking // ignore: cast_nullable_to_non_nullable as bool,isFixing: null == isFixing ? _self.isFixing : isFixing // ignore: cast_nullable_to_non_nullable as bool,lastScreenInfo: null == lastScreenInfo ? _self.lastScreenInfo : lastScreenInfo // ignore: cast_nullable_to_non_nullable as String,isFixingString: null == isFixingString ? _self.isFixingString : isFixingString // ignore: cast_nullable_to_non_nullable as String,checkResult: freezed == checkResult ? _self.checkResult : checkResult // ignore: cast_nullable_to_non_nullable -as List>?, +as List>?,customLogFilePath: freezed == customLogFilePath ? _self.customLogFilePath : customLogFilePath // ignore: cast_nullable_to_non_nullable +as String?,customLogFileContent: freezed == customLogFileContent ? _self.customLogFileContent : customLogFileContent // ignore: cast_nullable_to_non_nullable +as List?, )); } @@ -154,10 +156,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult, String? customLogFilePath, List? customLogFileContent)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _HomeGameDoctorState() when $default != null: -return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult);case _: +return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult,_that.customLogFilePath,_that.customLogFileContent);case _: return orElse(); } @@ -175,10 +177,10 @@ return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFix /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult, String? customLogFilePath, List? customLogFileContent) $default,) {final _that = this; switch (_that) { case _HomeGameDoctorState(): -return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult);case _: +return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult,_that.customLogFilePath,_that.customLogFileContent);case _: throw StateError('Unexpected subclass'); } @@ -195,10 +197,10 @@ return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFix /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult, String? customLogFilePath, List? customLogFileContent)? $default,) {final _that = this; switch (_that) { case _HomeGameDoctorState() when $default != null: -return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult);case _: +return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFixingString,_that.checkResult,_that.customLogFilePath,_that.customLogFileContent);case _: return null; } @@ -210,7 +212,7 @@ return $default(_that.isChecking,_that.isFixing,_that.lastScreenInfo,_that.isFix class _HomeGameDoctorState implements HomeGameDoctorState { - _HomeGameDoctorState({this.isChecking = false, this.isFixing = false, this.lastScreenInfo = "", this.isFixingString = "", final List>? checkResult}): _checkResult = checkResult; + _HomeGameDoctorState({this.isChecking = false, this.isFixing = false, this.lastScreenInfo = "", this.isFixingString = "", final List>? checkResult, this.customLogFilePath, final List? customLogFileContent}): _checkResult = checkResult,_customLogFileContent = customLogFileContent; @override@JsonKey() final bool isChecking; @@ -226,6 +228,16 @@ class _HomeGameDoctorState implements HomeGameDoctorState { return EqualUnmodifiableListView(value); } +@override final String? customLogFilePath; + final List? _customLogFileContent; +@override List? get customLogFileContent { + final value = _customLogFileContent; + if (value == null) return null; + if (_customLogFileContent is EqualUnmodifiableListView) return _customLogFileContent; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); +} + /// Create a copy of HomeGameDoctorState /// with the given fields replaced by the non-null parameter values. @@ -237,16 +249,16 @@ _$HomeGameDoctorStateCopyWith<_HomeGameDoctorState> get copyWith => __$HomeGameD @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeGameDoctorState&&(identical(other.isChecking, isChecking) || other.isChecking == isChecking)&&(identical(other.isFixing, isFixing) || other.isFixing == isFixing)&&(identical(other.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.isFixingString, isFixingString) || other.isFixingString == isFixingString)&&const DeepCollectionEquality().equals(other._checkResult, _checkResult)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeGameDoctorState&&(identical(other.isChecking, isChecking) || other.isChecking == isChecking)&&(identical(other.isFixing, isFixing) || other.isFixing == isFixing)&&(identical(other.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.isFixingString, isFixingString) || other.isFixingString == isFixingString)&&const DeepCollectionEquality().equals(other._checkResult, _checkResult)&&(identical(other.customLogFilePath, customLogFilePath) || other.customLogFilePath == customLogFilePath)&&const DeepCollectionEquality().equals(other._customLogFileContent, _customLogFileContent)); } @override -int get hashCode => Object.hash(runtimeType,isChecking,isFixing,lastScreenInfo,isFixingString,const DeepCollectionEquality().hash(_checkResult)); +int get hashCode => Object.hash(runtimeType,isChecking,isFixing,lastScreenInfo,isFixingString,const DeepCollectionEquality().hash(_checkResult),customLogFilePath,const DeepCollectionEquality().hash(_customLogFileContent)); @override String toString() { - return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult)'; + return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult, customLogFilePath: $customLogFilePath, customLogFileContent: $customLogFileContent)'; } @@ -257,7 +269,7 @@ abstract mixin class _$HomeGameDoctorStateCopyWith<$Res> implements $HomeGameDoc factory _$HomeGameDoctorStateCopyWith(_HomeGameDoctorState value, $Res Function(_HomeGameDoctorState) _then) = __$HomeGameDoctorStateCopyWithImpl; @override @useResult $Res call({ - bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult + bool isChecking, bool isFixing, String lastScreenInfo, String isFixingString, List>? checkResult, String? customLogFilePath, List? customLogFileContent }); @@ -274,14 +286,16 @@ class __$HomeGameDoctorStateCopyWithImpl<$Res> /// Create a copy of HomeGameDoctorState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? isChecking = null,Object? isFixing = null,Object? lastScreenInfo = null,Object? isFixingString = null,Object? checkResult = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? isChecking = null,Object? isFixing = null,Object? lastScreenInfo = null,Object? isFixingString = null,Object? checkResult = freezed,Object? customLogFilePath = freezed,Object? customLogFileContent = freezed,}) { return _then(_HomeGameDoctorState( isChecking: null == isChecking ? _self.isChecking : isChecking // ignore: cast_nullable_to_non_nullable as bool,isFixing: null == isFixing ? _self.isFixing : isFixing // ignore: cast_nullable_to_non_nullable as bool,lastScreenInfo: null == lastScreenInfo ? _self.lastScreenInfo : lastScreenInfo // ignore: cast_nullable_to_non_nullable as String,isFixingString: null == isFixingString ? _self.isFixingString : isFixingString // ignore: cast_nullable_to_non_nullable as String,checkResult: freezed == checkResult ? _self._checkResult : checkResult // ignore: cast_nullable_to_non_nullable -as List>?, +as List>?,customLogFilePath: freezed == customLogFilePath ? _self.customLogFilePath : customLogFilePath // ignore: cast_nullable_to_non_nullable +as String?,customLogFileContent: freezed == customLogFileContent ? _self._customLogFileContent : customLogFileContent // ignore: cast_nullable_to_non_nullable +as List?, )); } diff --git a/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart b/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart index 3a5e722..6efbdd7 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart @@ -42,7 +42,7 @@ final class HomeGameDoctorUIModelProvider } String _$homeGameDoctorUIModelHash() => - r'7035b501860e9d8c3fdfb91370311760120af115'; + r'8b969c4638fb07b8224dd60b3f04fa29742c4ae8'; abstract class _$HomeGameDoctorUIModel extends $Notifier { HomeGameDoctorState build(); diff --git a/lib/ui/home/home_ui.dart b/lib/ui/home/home_ui.dart index 2720932..1670672 100644 --- a/lib/ui/home/home_ui.dart +++ b/lib/ui/home/home_ui.dart @@ -11,7 +11,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/generated/no_l10n_strings.dart'; import 'package:starcitizen_doctor/ui/guide/guide_ui.dart'; -import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -32,7 +31,7 @@ class HomeUI extends HookConsumerWidget { ref.watch(localizationUIModelProvider); useEffect(() { - _checkGuide(context, model); + // _checkGuide(context, model); return null; }, const []); @@ -118,25 +117,25 @@ class HomeUI extends HookConsumerWidget { children: [ Text(S.current.home_install_location), const SizedBox(width: 12), - Button( - onPressed: () async { - await context.push("/guide"); - await model.reScanPath(); - }, - child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.settings)), - ), - const SizedBox(width: 6), + // Button( + // onPressed: () async { + // await context.push("/guide"); + // await model.reScanPath(); + // }, + // child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.settings)), + // ), + // const SizedBox(width: 6), Expanded( child: ComboBox( value: homeState.scInstalledPath, isExpanded: true, items: [ - ComboBoxItem(value: "not_install", child: Text(S.current.home_not_installed_or_failed)), - for (final path in homeState.scInstallPaths) - ComboBoxItem( - value: path, - child: Row(children: [Text(path)]), - ), + ComboBoxItem(value: "not_install", child: Text("您的电脑")), + // for (final path in homeState.scInstallPaths) + // ComboBoxItem( + // value: path, + // child: Row(children: [Text(path)]), + // ), ], onChanged: model.onChangeInstallPath, ), @@ -714,13 +713,8 @@ class HomeUI extends HookConsumerWidget { } Future _onMenuTap(BuildContext context, String key, HomeUIModelState homeState, WidgetRef ref) async { - String gameInstallReqInfo = S.current.home_action_info_valid_install_location_required; switch (key) { case "localization": - if (homeState.scInstalledPath == "not_install") { - ToolsUIModel.rsiEnhance(context, showNotGameInstallMsg: true); - break; - } final model = ref.watch(homeUIModelProvider.notifier); model.checkLocalizationUpdate(); await showDialog( @@ -731,14 +725,10 @@ class HomeUI extends HookConsumerWidget { model.checkLocalizationUpdate(skipReload: true); break; case "performance": - if (homeState.scInstalledPath == "not_install") { - showToast(context, gameInstallReqInfo); - break; - } - context.push("/index/$key"); + context.push("/$key"); break; default: - context.push("/index/$key"); + context.push("/$key"); } } @@ -771,42 +761,7 @@ class HomeUI extends HookConsumerWidget { HomeUIModel model, WidgetRef ref, ) async { - final localizationState = ref.read(localizationUIModelProvider); - if (localizationState.communityInputMethodLanguageData == null) { - showToast(context, S.current.input_method_feature_maintenance); - return; - } - if (localizationState.installedCommunityInputMethodSupportVersion == null) { - final userOK = await showConfirmDialogs( - context, - S.current.input_method_community_input_method_not_installed, - Text(S.current.input_method_install_community_input_method_prompt), - ); - if (userOK) { - if (!context.mounted) return; - () async { - await _onMenuTap(context, 'localization', homeState, ref); - final localizationState = ref.read(localizationUIModelProvider); - if (localizationState.installedCommunityInputMethodSupportVersion != null) { - await Future.delayed(Duration(milliseconds: 300)); - if (!context.mounted) return; - await _goInputMethod(context, model); - return; - } - }(); - - await Future.delayed(Duration(milliseconds: 300)); - final localizationModel = ref.read(localizationUIModelProvider.notifier); - if (!context.mounted) return; - localizationModel.checkReinstall(context); - } - return; - } - await _goInputMethod(context, model); - } - - Future _goInputMethod(BuildContext context, HomeUIModel model) async { - await showDialog(context: context, builder: (context) => const InputMethodDialogUI()); + showToast(context, "请使用完整版体验"); } } diff --git a/lib/ui/home/home_ui_model.dart b/lib/ui/home/home_ui_model.dart index 38e1105..c84574c 100644 --- a/lib/ui/home/home_ui_model.dart +++ b/lib/ui/home/home_ui_model.dart @@ -120,7 +120,12 @@ class HomeUIModel extends _$HomeUIModel { for (var node in h.body!.nodes) { if (node is html_dom.Element) { if (node.localName == "img") { - return node.attributes["src"]?.trim() ?? ""; + final image = node.attributes["src"]?.trim() ?? ""; + var updatedImage = image.replaceAllMapped( + RegExp(r'http(s)?://i(\d+)\.hdslb\.com/bfs/'), + (match) => 'https://web-proxy.scbox.xkeyc.cn/bfs${match[2]}/bfs/', + ); + return updatedImage; } } } diff --git a/lib/ui/home/home_ui_model.g.dart b/lib/ui/home/home_ui_model.g.dart index 00590f0..d49298d 100644 --- a/lib/ui/home/home_ui_model.g.dart +++ b/lib/ui/home/home_ui_model.g.dart @@ -41,7 +41,7 @@ final class HomeUIModelProvider } } -String _$homeUIModelHash() => r'9dc8191f358c2d8e21ed931b3755e08ce394558e'; +String _$homeUIModelHash() => r'8ceec2769c0cbc2df62f152224e793a3d10729a8'; abstract class _$HomeUIModel extends $Notifier { HomeUIModelState build(); diff --git a/lib/ui/home/localization/advanced_localization_ui_model.g.dart b/lib/ui/home/localization/advanced_localization_ui_model.g.dart index 38fb741..4b5d99d 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.g.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.g.dart @@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider } String _$advancedLocalizationUIModelHash() => - r'2f890c854bc56e506c441acabc2014438a163617'; + r'c7cca8935ac7df2281e83297b11b6b82d94f7a59'; abstract class _$AdvancedLocalizationUIModel extends $Notifier { diff --git a/lib/ui/home/localization/localization_dialog_ui.dart b/lib/ui/home/localization/localization_dialog_ui.dart index 1f71c60..b581321 100644 --- a/lib/ui/home/localization/localization_dialog_ui.dart +++ b/lib/ui/home/localization/localization_dialog_ui.dart @@ -30,13 +30,9 @@ class LocalizationDialogUI extends HookConsumerWidget { return ContentDialog( title: makeTitle(context, model, state), constraints: BoxConstraints( - maxWidth: MediaQuery - .of(context) - .size - .width * .7, minHeight: MediaQuery - .of(context) - .size - .height * .9), + maxWidth: MediaQuery.of(context).size.width * .7, + minHeight: MediaQuery.of(context).size.height * .9, + ), content: Padding( padding: const EdgeInsets.only(left: 12, right: 12, top: 12), child: SingleChildScrollView( @@ -44,132 +40,57 @@ class LocalizationDialogUI extends HookConsumerWidget { children: [ AnimatedSize( duration: const Duration(milliseconds: 130), - child: state.patchStatus?.key == true && - state.patchStatus?.value == S.current.home_action_info_game_built_in + child: + state.patchStatus?.key == true && + state.patchStatus?.value == S.current.home_action_info_game_built_in ? Padding( - padding: const EdgeInsets.only(bottom: 12), - child: InfoBar( - title: Text(S.current.home_action_info_warning), - content: Text(S.current.localization_info_machine_translation_warning), - severity: InfoBarSeverity.info, - style: InfoBarThemeData(decoration: (severity) { - return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0)); - }, iconColor: (severity) { - return Colors.white; - }), - ), - ) - : SizedBox( - width: MediaQuery - .of(context) - .size - .width, - ), + padding: const EdgeInsets.only(bottom: 12), + child: InfoBar( + title: Text(S.current.home_action_info_warning), + content: Text(S.current.localization_info_machine_translation_warning), + severity: InfoBarSeverity.info, + style: InfoBarThemeData( + decoration: (severity) { + return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0)); + }, + iconColor: (severity) { + return Colors.white; + }, + ), + ), + ) + : SizedBox(width: MediaQuery.of(context).size.width), ), makeToolsListContainer(context, model, state), - makeListContainer( - S.current.localization_info_translation, - [ - if (state.patchStatus == null) - makeLoading(context) - else - ...[ - const SizedBox(height: 6), - Row( - children: [ - Center( - child: Text(S.current.localization_info_enabled( - LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "")), - ), - const Spacer(), - ToggleSwitch( - checked: state.patchStatus?.key == true, - onChanged: model.updateLangCfg, - ) - ], - ), - const SizedBox(height: 12), - Row( - children: [ - Expanded( - child: Row( - children: [ - Text(S.current.localization_info_installed_version( - "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S - .current.home_localization_msg_version_advanced : ""}")), - SizedBox(width: 24), - if (state.installedCommunityInputMethodSupportVersion != null) - Text( - S.current.input_method_community_input_method_support_version( - state.installedCommunityInputMethodSupportVersion ?? "?"), - ) - ], - ), - ), - if (state.patchStatus?.value != S.current.home_action_info_game_built_in) - Row( - children: [ - Button( - onPressed: model.goFeedback, - child: Padding( - padding: const EdgeInsets.all(4), - child: Row( - children: [ - const Icon(FluentIcons.feedback), - const SizedBox(width: 6), - Text(S.current.localization_action_translation_feedback), - ], - ), - )), - const SizedBox(width: 16), - Button( - onPressed: model.doDelIniFile(), - child: Padding( - padding: const EdgeInsets.all(4), - child: Row( - children: [ - const Icon(FluentIcons.delete), - const SizedBox(width: 6), - Text(S.current.localization_action_uninstall_translation), - ], - ), - )), - ], - ), - ], - ), - const SizedBox(height: 12), - Container( - color: Colors.white.withValues(alpha: .1), - height: 1, - ), - const SizedBox(height: 12), - if (state.apiLocalizationData == null) - makeLoading(context) - else - if (state.apiLocalizationData!.isEmpty) - Center( - child: Text( - S.current.localization_info_no_translation_available, - style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .8)), - ), - ) - else - AlignedGridView.count( - crossAxisCount: 2, - crossAxisSpacing: 12, - mainAxisSpacing: 12, - itemBuilder: (BuildContext context, int index) { - final item = state.apiLocalizationData!.entries.elementAt(index); - return makeRemoteList(context, model, item, state, index); - }, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: state.apiLocalizationData?.length ?? 0, - ) - ], - ], - context), + makeListContainer(S.current.localization_info_translation, [ + if (state.patchStatus == null) + makeLoading(context) + else ...[ + const SizedBox(height: 6), + if (state.apiLocalizationData == null) + makeLoading(context) + else if (state.apiLocalizationData!.isEmpty) + Center( + child: Text( + S.current.localization_info_no_translation_available, + style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .8)), + ), + ) + else + AlignedGridView.count( + crossAxisCount: 2, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + itemBuilder: (BuildContext context, int index) { + final item = state.apiLocalizationData!.entries.elementAt(index); + return makeRemoteList(context, model, item, state, index); + }, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: state.apiLocalizationData?.length ?? 0, + ), + ], + ], context), ], ), ), @@ -177,8 +98,13 @@ class LocalizationDialogUI extends HookConsumerWidget { ); } - Widget makeRemoteList(BuildContext context, LocalizationUIModel model, MapEntry item, - LocalizationUIState state, int index) { + Widget makeRemoteList( + BuildContext context, + LocalizationUIModel model, + MapEntry item, + LocalizationUIState state, + int index, + ) { final isWorking = state.workingVersion.isNotEmpty; final isMineWorking = state.workingVersion == item.key; final isInstalled = state.patchStatus?.value == item.key; @@ -207,10 +133,7 @@ class LocalizationDialogUI extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "${item.value.info}", - style: const TextStyle(fontSize: 19), - ), + Text("${item.value.info}", style: const TextStyle(fontSize: 19)), const SizedBox(height: 4), Text( S.current.localization_info_version_number(item.value.versionName ?? ""), @@ -230,40 +153,30 @@ class LocalizationDialogUI extends HookConsumerWidget { ), ), if (isMineWorking) - const Padding( - padding: EdgeInsets.only(right: 12), - child: ProgressRing(), - ) - else - ...[ - Icon( - isInstalled - ? FluentIcons.check_mark - : isItemEnabled - ? FluentIcons.download - : FluentIcons.disable_updates, - color: Colors.white.withValues(alpha: .8), - size: 18, - ), - const SizedBox(width: 6), - Text( - isInstalled - ? S.current.localization_info_installed - : (isItemEnabled - ? S.current.localization_action_install - : S.current.localization_info_unavailable), - style: TextStyle( - color: Colors.white.withValues(alpha: .8), - ), - ), - const SizedBox(width: 6), - if ((!isInstalled) && isItemEnabled) - Icon( - FluentIcons.chevron_right, - size: 14, - color: Colors.white.withValues(alpha: .6), - ) - ] + const Padding(padding: EdgeInsets.only(right: 12), child: ProgressRing()) + else ...[ + Icon( + isInstalled + ? FluentIcons.check_mark + : isItemEnabled + ? FluentIcons.download + : FluentIcons.disable_updates, + color: Colors.white.withValues(alpha: .8), + size: 18, + ), + const SizedBox(width: 6), + Text( + isInstalled + ? S.current.localization_info_installed + : (isItemEnabled + ? S.current.localization_action_install + : S.current.localization_info_unavailable), + style: TextStyle(color: Colors.white.withValues(alpha: .8)), + ), + const SizedBox(width: 6), + if ((!isInstalled) && isItemEnabled) + Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)), + ], ], ), if (item.value.note != null) ...[ @@ -272,10 +185,7 @@ class LocalizationDialogUI extends HookConsumerWidget { "${item.value.note}", maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Colors.white.withValues(alpha: .4), - fontSize: 13, - ), + style: TextStyle(color: Colors.white.withValues(alpha: .4), fontSize: 13), ), ], ], @@ -286,16 +196,20 @@ class LocalizationDialogUI extends HookConsumerWidget { ); } - Widget makeListContainer(String title, List children, BuildContext context, - {List actions = const [], bool gridViewMode = false, int gridViewCrossAxisCount = 2}) { + Widget makeListContainer( + String title, + List children, + BuildContext context, { + List actions = const [], + bool gridViewMode = false, + int gridViewCrossAxisCount = 2, + }) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: AnimatedSize( duration: const Duration(milliseconds: 130), child: Container( - decoration: BoxDecoration(color: FluentTheme - .of(context) - .cardColor, borderRadius: BorderRadius.circular(7)), + decoration: BoxDecoration(color: FluentTheme.of(context).cardColor, borderRadius: BorderRadius.circular(7)), child: Padding( padding: const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24), child: Column( @@ -303,21 +217,13 @@ class LocalizationDialogUI extends HookConsumerWidget { children: [ Row( children: [ - Text( - title, - style: const TextStyle(fontSize: 22), - ), + Text(title, style: const TextStyle(fontSize: 22)), const Spacer(), if (actions.isNotEmpty) ...actions, ], ), - const SizedBox( - height: 6, - ), - Container( - color: Colors.white.withValues(alpha: .1), - height: 1, - ), + const SizedBox(height: 6), + Container(color: Colors.white.withValues(alpha: .1), height: 1), const SizedBox(height: 12), if (gridViewMode) AlignedGridView.count( @@ -332,7 +238,7 @@ class LocalizationDialogUI extends HookConsumerWidget { itemCount: children.length, ) else - ...children + ...children, ], ), ), @@ -344,55 +250,54 @@ class LocalizationDialogUI extends HookConsumerWidget { Widget makeTitle(BuildContext context, LocalizationUIModel model, LocalizationUIState state) { return Row( children: [ - IconButton( - icon: const Icon( - FluentIcons.back, - size: 22, - ), - onPressed: model.onBack(context)), + IconButton(icon: const Icon(FluentIcons.back, size: 22), onPressed: model.onBack(context)), const SizedBox(width: 12), Text(S.current.home_action_localization_management), const SizedBox(width: 24), - Text( - "${model.getScInstallPath()}", - style: const TextStyle(fontSize: 13), - ), const Spacer(), SizedBox( height: 36, child: Row( children: [ - Text( - S.current.localization_info_language, - style: const TextStyle(fontSize: 16), - ), + Text(S.current.localization_info_language, style: const TextStyle(fontSize: 16)), const SizedBox(width: 12), ComboBox( value: state.selectedLanguage, items: [ for (final lang in LocalizationUIModel.languageSupport.entries) - ComboBoxItem( - value: lang.key, - child: Text(lang.value), - ) + ComboBoxItem(value: lang.key, child: Text(lang.value)), ], onChanged: state.workingVersion.isNotEmpty ? null : (v) { - if (v == null) return; - model.selectLang(v); - }, - ) + if (v == null) return; + model.selectLang(v); + }, + ), + const SizedBox(width: 12), + const Text("频道选择", style: TextStyle(fontSize: 16)), + const SizedBox(width: 12), + ComboBox( + value: state.selectedChannel, + items: const [ + ComboBoxItem(value: "LIVE", child: Text("LIVE")), + ComboBoxItem(value: "PTU", child: Text("PTU")), + ], + onChanged: state.workingVersion.isNotEmpty + ? null + : (v) { + if (v == null) return; + model.selectChannel(v); + }, + ), ], ), ), const SizedBox(width: 12), Button( - onPressed: model.doRefresh(), - child: const Padding( - padding: EdgeInsets.all(6), - child: Icon(FluentIcons.refresh), - )), + onPressed: model.doRefresh(), + child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.refresh)), + ), ], ); } @@ -400,85 +305,77 @@ class LocalizationDialogUI extends HookConsumerWidget { Widget makeToolsListContainer(BuildContext context, LocalizationUIModel model, LocalizationUIState state) { final toolsMenu = { "launcher_mod": ( - const Icon(FluentIcons.c_plus_plus, size: 24), - (S.current.home_localization_action_rsi_launcher_localization), - ), - "advanced": ( - const Icon(FluentIcons.queue_advanced, size: 24), - (S.current.home_localization_action_advanced), + const Icon(FluentIcons.c_plus_plus, size: 24), + (S.current.home_localization_action_rsi_launcher_localization), ), + "advanced": (const Icon(FluentIcons.queue_advanced, size: 24), (S.current.home_localization_action_advanced)), "custom_files": ( - const Icon(FluentIcons.custom_activity, size: 24), - (S.current.home_localization_action_install_customize), + const Icon(FluentIcons.custom_activity, size: 24), + (S.current.home_localization_action_install_customize), ), }; final enableTap = state.workingVersion.isEmpty; return makeListContainer( - S.current.home_localization_title_localization_tools, - [ - for (final item in toolsMenu.entries) - Tilt( - disable: !enableTap, - shadowConfig: const ShadowConfig(maxIntensity: .3), - borderRadius: BorderRadius.circular(7), - child: GestureDetector( - onTap: enableTap - ? () async { - switch (item.key) { - case "launcher_mod": - ToolsUIModel.rsiEnhance(context); - break; - case "advanced": - context.push("/index/advanced_localization"); - break; - case "custom_files": - final sb = await showDialog( - context: context, - builder: (BuildContext context) => const LocalizationFromFileDialogUI(), - ); - if (sb is (StringBuffer, bool)) { - if (!context.mounted) return; - await model.installFormString( - sb.$1, - S.current.localization_info_custom_files, - isEnableCommunityInputMethod: sb.$2, - context: context, - ); + S.current.home_localization_title_localization_tools, + [ + for (final item in toolsMenu.entries) + Tilt( + disable: !enableTap, + shadowConfig: const ShadowConfig(maxIntensity: .3), + borderRadius: BorderRadius.circular(7), + child: GestureDetector( + onTap: enableTap + ? () async { + switch (item.key) { + case "launcher_mod": + ToolsUIModel.rsiEnhance(context); + break; + case "advanced": + context.push("/advanced_localization"); + break; + case "custom_files": + final sb = await showDialog( + context: context, + builder: (BuildContext context) => const LocalizationFromFileDialogUI(), + ); + if (sb is (StringBuffer, bool)) { + if (!context.mounted) return; + await model.installFormString( + sb.$1, + S.current.localization_info_custom_files, + isEnableCommunityInputMethod: sb.$2, + context: context, + ); + } + break; } - break; - } - } - : null, - child: Container( - decoration: BoxDecoration( - color: FluentTheme - .of(context) - .cardColor, - borderRadius: BorderRadius.circular(7), - ), - padding: const EdgeInsets.all(12), - child: Row( - children: [ - item.value.$1, - const SizedBox(width: 12), - Text(item.value.$2), - const SizedBox(width: 12), - const Spacer(), - Icon( - FluentIcons.chevron_right, - size: 14, - color: Colors.white.withValues(alpha: .6), - ) - ], - ), + } + : null, + child: Container( + decoration: BoxDecoration( + color: FluentTheme.of(context).cardColor, + borderRadius: BorderRadius.circular(7), + ), + padding: const EdgeInsets.all(12), + child: Row( + children: [ + item.value.$1, + const SizedBox(width: 12), + Text(item.value.$2), + const SizedBox(width: 12), + const Spacer(), + Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)), + ], ), ), ), - ], - context, - gridViewMode: true, - gridViewCrossAxisCount: 3); + ), + ], + context, + gridViewMode: true, + gridViewCrossAxisCount: 3, + ); } } diff --git a/lib/ui/home/localization/localization_ui_model.dart b/lib/ui/home/localization/localization_ui_model.dart index 680dd18..fa98b36 100644 --- a/lib/ui/home/localization/localization_ui_model.dart +++ b/lib/ui/home/localization/localization_ui_model.dart @@ -2,10 +2,11 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; -import 'package:archive/archive_io.dart'; +import 'package:archive/archive.dart'; import 'package:fluent_ui/fluent_ui.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' show kIsWeb, compute; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_ce/hive.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -23,8 +24,8 @@ import 'package:starcitizen_doctor/ui/home/home_ui_model.dart'; import 'package:starcitizen_doctor/ui/tools/dialogs/vehicle_sorting_dialog_ui.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; import 'package:url_launcher/url_launcher_string.dart'; - -import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; +import 'dart:js_interop'; +import 'package:web/web.dart' as web; part 'localization_ui_model.g.dart'; @@ -34,6 +35,7 @@ part 'localization_ui_model.freezed.dart'; abstract class LocalizationUIState with _$LocalizationUIState { factory LocalizationUIState({ String? selectedLanguage, + @Default("LIVE") String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, @@ -46,10 +48,7 @@ abstract class LocalizationUIState with _$LocalizationUIState { @riverpod class LocalizationUIModel extends _$LocalizationUIModel { - static const languageSupport = { - "chinese_(simplified)": NoL10n.langZHS, - "chinese_(traditional)": NoL10n.langZHT, - }; + static const languageSupport = {"chinese_(simplified)": NoL10n.langZHS, "chinese_(traditional)": NoL10n.langZHT}; Directory get _downloadDir => Directory("${appGlobalState.applicationSupportDir}\\Localizations"); @@ -71,7 +70,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future _init() async { - if (_scInstallPath == "not_install") { + if (!kIsWeb && _scInstallPath == "not_install") { return; } ref.onDispose(() { @@ -108,7 +107,16 @@ class LocalizationUIModel extends _$LocalizationUIModel { Future _loadData() async { _allVersionLocalizationData.clear(); - await _updateStatus(); + if (!kIsWeb) { + await _updateStatus(); + } else { + await Future.delayed(Duration(milliseconds: 100)); + state = state.copyWith( + patchStatus: MapEntry(false, S.current.home_action_info_game_built_in), + isInstalledAdvanced: false, + installedCommunityInputMethodSupportVersion: null, + ); + } await _loadCommunityInputMethodData(); for (var lang in languageSupport.keys) { final l = await Api.getScLocalizationData(lang).unwrap(); @@ -116,7 +124,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (lang == state.selectedLanguage) { final apiLocalizationData = {}; for (var element in l) { - final isPTU = !_scInstallPath.contains("LIVE"); + final isPTU = !state.selectedChannel.contains("LIVE"); if (isPTU && element.gameChannel == "PTU") { apiLocalizationData[element.versionName ?? ""] = element; } else if (!isPTU && element.gameChannel == "PU") { @@ -135,14 +143,20 @@ class LocalizationUIModel extends _$LocalizationUIModel { } void checkUserCfg(BuildContext context) async { + // Web 版本不支持此功能 + if (kIsWeb) return; + final userCfgFile = File("$_scInstallPath\\USER.cfg"); if (await userCfgFile.exists()) { final cfgString = await userCfgFile.readAsString(); if (cfgString.contains("g_language") && !cfgString.contains("g_language=${state.selectedLanguage}")) { if (!context.mounted) return; - final ok = await showConfirmDialogs(context, S.current.localization_info_remove_incompatible_translation_params, - Text(S.current.localization_info_incompatible_translation_params_warning), - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + final ok = await showConfirmDialogs( + context, + S.current.localization_info_remove_incompatible_translation_params, + Text(S.current.localization_info_incompatible_translation_params_warning), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); if (ok == true) { var finalString = ""; for (var item in cfgString.split("\n")) { @@ -160,6 +174,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future updateLangCfg(bool enable) async { + // Web 版本不支持此功能 + if (kIsWeb) return; + final selectedLanguage = state.selectedLanguage!; final status = await _getLangCfgEnableLang(lang: selectedLanguage); final exists = await _cfgFile.exists(); @@ -216,7 +233,25 @@ class LocalizationUIModel extends _$LocalizationUIModel { launchUrlString(URLConf.feedbackUrl); } + Future genLangCfg() async { + final selectedLanguage = state.selectedLanguage!; + StringBuffer newStr = StringBuffer(); + if (!newStr.toString().contains("sys_languages=$selectedLanguage")) { + newStr.writeln("sys_languages=$selectedLanguage"); + } + if (!newStr.toString().contains("g_language=$selectedLanguage")) { + newStr.writeln("g_language=$selectedLanguage"); + } + if (!newStr.toString().contains("g_languageAudio")) { + newStr.writeln("g_languageAudio=english"); + } + return newStr.toString(); + } + VoidCallback? doDelIniFile() { + // Web 版本不支持此功能 + if (kIsWeb) return null; + return () async { final iniFile = File("${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); if (await iniFile.exists()) await iniFile.delete(); @@ -238,7 +273,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { BuildContext? context, }) async { dPrint("LocalizationUIModel -> installFormString $versionName"); - final iniFile = File("${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); + if (versionName.isNotEmpty) { if (!globalIni.toString().endsWith("\n")) { globalIni.write("\n"); @@ -246,7 +281,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { String? communityInputMethodVersion; String? communityInputMethodSupportData; - if (isEnableCommunityInputMethod) { + if (isEnableCommunityInputMethod && !kIsWeb) { final data = state.communityInputMethodLanguageData; if (data != null) { communityInputMethodVersion = data.version; @@ -258,8 +293,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } if (communityInputMethodVersion != null) { - globalIni - .write("_starcitizen_doctor_localization_community_input_method_version=$communityInputMethodVersion\n"); + globalIni.write( + "_starcitizen_doctor_localization_community_input_method_version=$communityInputMethodVersion\n", + ); } if (communityInputMethodSupportData != null) { for (var line in communityInputMethodSupportData.split("\n")) { @@ -274,17 +310,29 @@ class LocalizationUIModel extends _$LocalizationUIModel { var iniStringData = globalIni.toString().trim(); + // 载具排序对话框 - 在生成最终文件前执行 if ((context?.mounted ?? false) && isEnableVehicleSorting) { if (!context!.mounted) return; final iniStringDataVN = ValueNotifier(iniStringData); - final ok = await showConfirmDialogs(context, S.current.tools_vehicle_sorting_title, VehicleSortingDialogUi(iniStringData: iniStringDataVN),constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * .75, - )); + final ok = await showConfirmDialogs( + context, + S.current.tools_vehicle_sorting_title, + VehicleSortingDialogUi(iniStringData: iniStringDataVN), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .75), + ); if (ok) { iniStringData = iniStringDataVN.value; } } + // Web 版本生成下载文件 + if (kIsWeb) { + await _generateAndDownloadWebFile(iniStringData, versionName); + return; + } + + final iniFile = File("${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); + /// write ini if (await iniFile.exists()) { await iniFile.delete(); @@ -295,6 +343,28 @@ class LocalizationUIModel extends _$LocalizationUIModel { await _updateStatus(); } + Future _generateAndDownloadWebFile(String iniStringData, String versionName) async { + final selectedLanguage = state.selectedLanguage!; + final iniFileString = "\uFEFF$iniStringData"; + final cfg = await genLangCfg(); + final archive = Archive(); + archive.addFile( + ArchiveFile("data/Localization/$selectedLanguage/global.ini", iniFileString.length, utf8.encode(iniFileString)), + ); + archive.addFile(ArchiveFile("data/system.cfg", cfg.length, utf8.encode(cfg))); + final zip = await compute(_encodeZipFile, archive); + if (zip == null) return; + final blob = Blob.fromBytes(zip, opt: {"type": "application/zip"}); + final url = web.URL.createObjectURL(blob); + await Future.delayed(const Duration(seconds: 1)); + jsDownloadBlobFile(url, "Localization_$versionName.zip"); + } + + List? _encodeZipFile(Archive archive) { + final zip = ZipEncoder().encode(archive); + return zip; + } + Future?> getCommunityInputMethodSupportData() async { final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"; final iniFile = File(iniPath); @@ -348,10 +418,41 @@ class LocalizationUIModel extends _$LocalizationUIModel { return ("", ""); } - Future? doRemoteInstall(BuildContext context, ScLocalizationData value, - {bool isEnableCommunityInputMethod = false, bool isEnableVehicleSorting = false}) async { + Future? doRemoteInstall( + BuildContext context, + ScLocalizationData value, { + bool isEnableCommunityInputMethod = false, + bool isEnableVehicleSorting = false, + }) async { AnalyticsApi.touch("install_localization"); + // Web 版本直接下载并生成 ZIP + if (kIsWeb) { + try { + state = state.copyWith(workingVersion: value.versionName!); + final data = await downloadLocalizationFileForWeb(value); + await Future.delayed(const Duration(milliseconds: 300)); + // check file + final globalIni = await compute(readArchiveFromBytes, data); + if (globalIni.isEmpty) { + throw S.current.localization_info_corrupted_file; + } + if (!context.mounted) return; + await installFormString( + globalIni, + value.versionName ?? "", + isEnableCommunityInputMethod: isEnableCommunityInputMethod, + isEnableVehicleSorting: isEnableVehicleSorting, + context: context, + ); + } catch (e) { + if (!context.mounted) return; + await showToast(context, S.current.localization_info_installation_error(e)); + } + state = state.copyWith(workingVersion: ""); + return; + } + final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang"); try { state = state.copyWith(workingVersion: value.versionName!); @@ -385,7 +486,8 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future downloadOrGetCachedCommunityInputMethodSupportFile( - InputMethodApiLanguageData communityInputMethodData) async { + InputMethodApiLanguageData communityInputMethodData, + ) async { final lang = state.selectedLanguage ?? "_"; final box = await Hive.openBox("community_input_method_data"); final cachedVersion = box.get("${lang}_version"); @@ -410,6 +512,18 @@ class LocalizationUIModel extends _$LocalizationUIModel { } } + // Web 版本下载方法,返回字节数据 + Future downloadLocalizationFileForWeb(ScLocalizationData value) async { + dPrint("downloading file for web"); + final downloadUrl = "${URLConf.gitlabLocalizationUrl}/archive/${value.versionName}.tar.gz"; + final r = await RSHttp.get(downloadUrl); + if (r.statusCode == 200 && r.data != null) { + return r.data!; + } else { + throw "statusCode Error : ${r.statusCode}"; + } + } + static StringBuffer readArchive(String savePath) { final inputStream = InputFileStream(savePath); final output = GZipDecoder().decodeBytes(inputStream.toUint8List()); @@ -429,6 +543,25 @@ class LocalizationUIModel extends _$LocalizationUIModel { return globalIni; } + // Web 版本从字节数据读取 Archive + static StringBuffer readArchiveFromBytes(Uint8List data) { + final output = GZipDecoder().decodeBytes(data); + final archive = TarDecoder().decodeBytes(output); + StringBuffer globalIni = StringBuffer(""); + for (var element in archive.files) { + if (element.name.contains("global.ini")) { + if (element.rawContent == null) continue; + final stringContent = utf8.decode(element.rawContent!.readBytes()); + for (var value in (stringContent).split("\n")) { + final tv = value.trim(); + if (tv.isNotEmpty) globalIni.writeln(value); + } + } + } + archive.clear(); + return globalIni; + } + String? getScInstallPath() { return ref.read(homeUIModelProvider).scInstalledPath; } @@ -440,6 +573,13 @@ class LocalizationUIModel extends _$LocalizationUIModel { await appConfBox.put("localization_selectedLanguage", v); } + void selectChannel(String v) async { + state = state.copyWith(selectedChannel: v); + _loadData(); + final appConfBox = await Hive.openBox("app_conf"); + await appConfBox.put("localization_selectedChannel", v); + } + VoidCallback? onBack(BuildContext context) { if (state.workingVersion.isNotEmpty) return null; return () { @@ -456,14 +596,20 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future _updateStatus() async { + // Web 版本不支持此功能 + if (kIsWeb) return; + final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"; - final patchStatus = - MapEntry(await _getLangCfgEnableLang(lang: state.selectedLanguage!), await _getInstalledIniVersion(iniPath)); + final patchStatus = MapEntry( + await _getLangCfgEnableLang(lang: state.selectedLanguage!), + await _getInstalledIniVersion(iniPath), + ); final isInstalledAdvanced = await _checkAdvancedStatus(iniPath); final installedCommunityInputMethodSupportVersion = await getInstalledCommunityInputMethodSupportVersion(iniPath); dPrint( - "_updateStatus updateStatus: $patchStatus , isInstalledAdvanced: $isInstalledAdvanced ,installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion"); + "_updateStatus updateStatus: $patchStatus , isInstalledAdvanced: $isInstalledAdvanced ,installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion", + ); state = state.copyWith( patchStatus: patchStatus, @@ -473,6 +619,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future getInstalledCommunityInputMethodSupportVersion(String path) async { + // Web 版本不支持此功能 + if (kIsWeb) return null; + final iniFile = File(path); if (!await iniFile.exists()) { return null; @@ -488,6 +637,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future _checkAdvancedStatus(String path) async { + // Web 版本不支持此功能 + if (kIsWeb) return false; + final iniFile = File(path); if (!await iniFile.exists()) { return false; @@ -497,6 +649,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future _getLangCfgEnableLang({String lang = "", String gamePath = ""}) async { + // Web 版本不支持此功能 + if (kIsWeb) return false; + if (gamePath.isEmpty) { gamePath = _scInstallPath; } @@ -509,6 +664,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } static Future _getInstalledIniVersion(String iniPath) async { + // Web 版本不支持此功能 + if (kIsWeb) return S.current.home_action_info_game_built_in; + final iniFile = File(iniPath); if (!await iniFile.exists()) { return S.current.home_action_info_game_built_in; @@ -524,6 +682,9 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future> checkLangUpdate({bool skipReload = false}) async { + // Web 版本不支持此功能 + if (kIsWeb) return []; + if (_scInstallPath == "not_install") { return []; } @@ -571,6 +732,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future checkCommunityInputMethodUpdate() async { + if (kIsWeb) return; // Web 版本不支持自动更新检查 final cloudVersion = state.communityInputMethodLanguageData?.version; final localVersion = state.installedCommunityInputMethodSupportVersion; if (cloudVersion == null || localVersion == null) return; @@ -582,11 +744,14 @@ class LocalizationUIModel extends _$LocalizationUIModel { return; } await installFormString(StringBuffer(localIniString), versioName, isEnableCommunityInputMethod: true); - await win32.sendNotify( - summary: S.current.input_method_support_updated, - body: S.current.input_method_support_updated_to_version(cloudVersion), - appName: S.current.home_title_app_name, - appId: ConstConf.win32AppId); + if (!kIsWeb) { + // Web 版本不支持系统通知 + // await win32.sendNotify( + // summary: S.current.input_method_support_updated, + // body: S.current.input_method_support_updated_to_version(cloudVersion), + // appName: S.current.home_title_app_name, + // appId: ConstConf.win32AppId); + } } } @@ -595,7 +760,10 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future onRemoteInsTall( - BuildContext context, MapEntry item, LocalizationUIState state) async { + BuildContext context, + MapEntry item, + LocalizationUIState state, + ) async { final appBox = Hive.box("app_conf"); bool enableCommunityInputMethod = state.communityInputMethodLanguageData != null; bool isEnableVehicleSorting = appBox.get("vehicle_sorting", defaultValue: false) ?? false; @@ -646,9 +814,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { SizedBox(height: 12), Row( children: [ - Text( - S.current.input_method_install_community_input_method_support, - ), + Text(S.current.input_method_install_community_input_method_support), Spacer(), StatefulBuilder( builder: (BuildContext context, void Function(void Function()) setState) { @@ -662,15 +828,13 @@ class LocalizationUIModel extends _$LocalizationUIModel { }, ); }, - ) + ), ], ), SizedBox(height: 12), Row( children: [ - Text( - S.current.tools_vehicle_sorting_title, - ), + Text(S.current.tools_vehicle_sorting_title), Spacer(), StatefulBuilder( builder: (BuildContext context, void Function(void Function()) setState) { @@ -683,7 +847,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { }, ); }, - ) + ), ], ), ], @@ -717,4 +881,21 @@ class LocalizationUIModel extends _$LocalizationUIModel { } } } -} \ No newline at end of file +} + +// Web 平台支持 - JS 互操作 +@JS("Blob") +extension type Blob._(JSObject _) implements JSObject { + external factory Blob(JSArray blobParts, JSAny? options); + + factory Blob.fromBytes(List bytes, {Map? opt}) { + final data = Uint8List.fromList(bytes).buffer.toJS; + return Blob([data].toJS, opt?.jsify()); + } + + external JSArrayBuffer? get blobParts; + external JSObject? get options; +} + +@JS() +external void jsDownloadBlobFile(String blobUrl, String filename); diff --git a/lib/ui/home/localization/localization_ui_model.freezed.dart b/lib/ui/home/localization/localization_ui_model.freezed.dart index d6925d3..8102aab 100644 --- a/lib/ui/home/localization/localization_ui_model.freezed.dart +++ b/lib/ui/home/localization/localization_ui_model.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$LocalizationUIState { - String? get selectedLanguage; String? get installedCommunityInputMethodSupportVersion; InputMethodApiLanguageData? get communityInputMethodLanguageData; Map? get apiLocalizationData; String get workingVersion; MapEntry? get patchStatus; bool? get isInstalledAdvanced; List? get customizeList; + String? get selectedLanguage; String get selectedChannel; String? get installedCommunityInputMethodSupportVersion; InputMethodApiLanguageData? get communityInputMethodLanguageData; Map? get apiLocalizationData; String get workingVersion; MapEntry? get patchStatus; bool? get isInstalledAdvanced; List? get customizeList; /// Create a copy of LocalizationUIState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $LocalizationUIStateCopyWith get copyWith => _$Localization @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is LocalizationUIState&&(identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage)&&(identical(other.installedCommunityInputMethodSupportVersion, installedCommunityInputMethodSupportVersion) || other.installedCommunityInputMethodSupportVersion == installedCommunityInputMethodSupportVersion)&&(identical(other.communityInputMethodLanguageData, communityInputMethodLanguageData) || other.communityInputMethodLanguageData == communityInputMethodLanguageData)&&const DeepCollectionEquality().equals(other.apiLocalizationData, apiLocalizationData)&&(identical(other.workingVersion, workingVersion) || other.workingVersion == workingVersion)&&(identical(other.patchStatus, patchStatus) || other.patchStatus == patchStatus)&&(identical(other.isInstalledAdvanced, isInstalledAdvanced) || other.isInstalledAdvanced == isInstalledAdvanced)&&const DeepCollectionEquality().equals(other.customizeList, customizeList)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is LocalizationUIState&&(identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage)&&(identical(other.selectedChannel, selectedChannel) || other.selectedChannel == selectedChannel)&&(identical(other.installedCommunityInputMethodSupportVersion, installedCommunityInputMethodSupportVersion) || other.installedCommunityInputMethodSupportVersion == installedCommunityInputMethodSupportVersion)&&(identical(other.communityInputMethodLanguageData, communityInputMethodLanguageData) || other.communityInputMethodLanguageData == communityInputMethodLanguageData)&&const DeepCollectionEquality().equals(other.apiLocalizationData, apiLocalizationData)&&(identical(other.workingVersion, workingVersion) || other.workingVersion == workingVersion)&&(identical(other.patchStatus, patchStatus) || other.patchStatus == patchStatus)&&(identical(other.isInstalledAdvanced, isInstalledAdvanced) || other.isInstalledAdvanced == isInstalledAdvanced)&&const DeepCollectionEquality().equals(other.customizeList, customizeList)); } @override -int get hashCode => Object.hash(runtimeType,selectedLanguage,installedCommunityInputMethodSupportVersion,communityInputMethodLanguageData,const DeepCollectionEquality().hash(apiLocalizationData),workingVersion,patchStatus,isInstalledAdvanced,const DeepCollectionEquality().hash(customizeList)); +int get hashCode => Object.hash(runtimeType,selectedLanguage,selectedChannel,installedCommunityInputMethodSupportVersion,communityInputMethodLanguageData,const DeepCollectionEquality().hash(apiLocalizationData),workingVersion,patchStatus,isInstalledAdvanced,const DeepCollectionEquality().hash(customizeList)); @override String toString() { - return 'LocalizationUIState(selectedLanguage: $selectedLanguage, installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion, communityInputMethodLanguageData: $communityInputMethodLanguageData, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; + return 'LocalizationUIState(selectedLanguage: $selectedLanguage, selectedChannel: $selectedChannel, installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion, communityInputMethodLanguageData: $communityInputMethodLanguageData, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; } @@ -45,7 +45,7 @@ abstract mixin class $LocalizationUIStateCopyWith<$Res> { factory $LocalizationUIStateCopyWith(LocalizationUIState value, $Res Function(LocalizationUIState) _then) = _$LocalizationUIStateCopyWithImpl; @useResult $Res call({ - String? selectedLanguage, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList + String? selectedLanguage, String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList }); @@ -62,10 +62,11 @@ class _$LocalizationUIStateCopyWithImpl<$Res> /// Create a copy of LocalizationUIState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? selectedLanguage = freezed,Object? installedCommunityInputMethodSupportVersion = freezed,Object? communityInputMethodLanguageData = freezed,Object? apiLocalizationData = freezed,Object? workingVersion = null,Object? patchStatus = freezed,Object? isInstalledAdvanced = freezed,Object? customizeList = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? selectedLanguage = freezed,Object? selectedChannel = null,Object? installedCommunityInputMethodSupportVersion = freezed,Object? communityInputMethodLanguageData = freezed,Object? apiLocalizationData = freezed,Object? workingVersion = null,Object? patchStatus = freezed,Object? isInstalledAdvanced = freezed,Object? customizeList = freezed,}) { return _then(_self.copyWith( selectedLanguage: freezed == selectedLanguage ? _self.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable -as String?,installedCommunityInputMethodSupportVersion: freezed == installedCommunityInputMethodSupportVersion ? _self.installedCommunityInputMethodSupportVersion : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable +as String?,selectedChannel: null == selectedChannel ? _self.selectedChannel : selectedChannel // ignore: cast_nullable_to_non_nullable +as String,installedCommunityInputMethodSupportVersion: freezed == installedCommunityInputMethodSupportVersion ? _self.installedCommunityInputMethodSupportVersion : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable as String?,communityInputMethodLanguageData: freezed == communityInputMethodLanguageData ? _self.communityInputMethodLanguageData : communityInputMethodLanguageData // ignore: cast_nullable_to_non_nullable as InputMethodApiLanguageData?,apiLocalizationData: freezed == apiLocalizationData ? _self.apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable as Map?,workingVersion: null == workingVersion ? _self.workingVersion : workingVersion // ignore: cast_nullable_to_non_nullable @@ -157,10 +158,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String? selectedLanguage, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String? selectedLanguage, String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _LocalizationUIState() when $default != null: -return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: +return $default(_that.selectedLanguage,_that.selectedChannel,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: return orElse(); } @@ -178,10 +179,10 @@ return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSuppor /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String? selectedLanguage, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String? selectedLanguage, String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList) $default,) {final _that = this; switch (_that) { case _LocalizationUIState(): -return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: +return $default(_that.selectedLanguage,_that.selectedChannel,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: throw StateError('Unexpected subclass'); } @@ -198,10 +199,10 @@ return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSuppor /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? selectedLanguage, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? selectedLanguage, String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList)? $default,) {final _that = this; switch (_that) { case _LocalizationUIState() when $default != null: -return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: +return $default(_that.selectedLanguage,_that.selectedChannel,_that.installedCommunityInputMethodSupportVersion,_that.communityInputMethodLanguageData,_that.apiLocalizationData,_that.workingVersion,_that.patchStatus,_that.isInstalledAdvanced,_that.customizeList);case _: return null; } @@ -213,10 +214,11 @@ return $default(_that.selectedLanguage,_that.installedCommunityInputMethodSuppor class _LocalizationUIState implements LocalizationUIState { - _LocalizationUIState({this.selectedLanguage, this.installedCommunityInputMethodSupportVersion, this.communityInputMethodLanguageData, final Map? apiLocalizationData, this.workingVersion = "", this.patchStatus, this.isInstalledAdvanced, final List? customizeList}): _apiLocalizationData = apiLocalizationData,_customizeList = customizeList; + _LocalizationUIState({this.selectedLanguage, this.selectedChannel = "LIVE", this.installedCommunityInputMethodSupportVersion, this.communityInputMethodLanguageData, final Map? apiLocalizationData, this.workingVersion = "", this.patchStatus, this.isInstalledAdvanced, final List? customizeList}): _apiLocalizationData = apiLocalizationData,_customizeList = customizeList; @override final String? selectedLanguage; +@override@JsonKey() final String selectedChannel; @override final String? installedCommunityInputMethodSupportVersion; @override final InputMethodApiLanguageData? communityInputMethodLanguageData; final Map? _apiLocalizationData; @@ -251,16 +253,16 @@ _$LocalizationUIStateCopyWith<_LocalizationUIState> get copyWith => __$Localizat @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocalizationUIState&&(identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage)&&(identical(other.installedCommunityInputMethodSupportVersion, installedCommunityInputMethodSupportVersion) || other.installedCommunityInputMethodSupportVersion == installedCommunityInputMethodSupportVersion)&&(identical(other.communityInputMethodLanguageData, communityInputMethodLanguageData) || other.communityInputMethodLanguageData == communityInputMethodLanguageData)&&const DeepCollectionEquality().equals(other._apiLocalizationData, _apiLocalizationData)&&(identical(other.workingVersion, workingVersion) || other.workingVersion == workingVersion)&&(identical(other.patchStatus, patchStatus) || other.patchStatus == patchStatus)&&(identical(other.isInstalledAdvanced, isInstalledAdvanced) || other.isInstalledAdvanced == isInstalledAdvanced)&&const DeepCollectionEquality().equals(other._customizeList, _customizeList)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocalizationUIState&&(identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage)&&(identical(other.selectedChannel, selectedChannel) || other.selectedChannel == selectedChannel)&&(identical(other.installedCommunityInputMethodSupportVersion, installedCommunityInputMethodSupportVersion) || other.installedCommunityInputMethodSupportVersion == installedCommunityInputMethodSupportVersion)&&(identical(other.communityInputMethodLanguageData, communityInputMethodLanguageData) || other.communityInputMethodLanguageData == communityInputMethodLanguageData)&&const DeepCollectionEquality().equals(other._apiLocalizationData, _apiLocalizationData)&&(identical(other.workingVersion, workingVersion) || other.workingVersion == workingVersion)&&(identical(other.patchStatus, patchStatus) || other.patchStatus == patchStatus)&&(identical(other.isInstalledAdvanced, isInstalledAdvanced) || other.isInstalledAdvanced == isInstalledAdvanced)&&const DeepCollectionEquality().equals(other._customizeList, _customizeList)); } @override -int get hashCode => Object.hash(runtimeType,selectedLanguage,installedCommunityInputMethodSupportVersion,communityInputMethodLanguageData,const DeepCollectionEquality().hash(_apiLocalizationData),workingVersion,patchStatus,isInstalledAdvanced,const DeepCollectionEquality().hash(_customizeList)); +int get hashCode => Object.hash(runtimeType,selectedLanguage,selectedChannel,installedCommunityInputMethodSupportVersion,communityInputMethodLanguageData,const DeepCollectionEquality().hash(_apiLocalizationData),workingVersion,patchStatus,isInstalledAdvanced,const DeepCollectionEquality().hash(_customizeList)); @override String toString() { - return 'LocalizationUIState(selectedLanguage: $selectedLanguage, installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion, communityInputMethodLanguageData: $communityInputMethodLanguageData, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; + return 'LocalizationUIState(selectedLanguage: $selectedLanguage, selectedChannel: $selectedChannel, installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion, communityInputMethodLanguageData: $communityInputMethodLanguageData, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; } @@ -271,7 +273,7 @@ abstract mixin class _$LocalizationUIStateCopyWith<$Res> implements $Localizatio factory _$LocalizationUIStateCopyWith(_LocalizationUIState value, $Res Function(_LocalizationUIState) _then) = __$LocalizationUIStateCopyWithImpl; @override @useResult $Res call({ - String? selectedLanguage, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList + String? selectedLanguage, String selectedChannel, String? installedCommunityInputMethodSupportVersion, InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, bool? isInstalledAdvanced, List? customizeList }); @@ -288,10 +290,11 @@ class __$LocalizationUIStateCopyWithImpl<$Res> /// Create a copy of LocalizationUIState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? selectedLanguage = freezed,Object? installedCommunityInputMethodSupportVersion = freezed,Object? communityInputMethodLanguageData = freezed,Object? apiLocalizationData = freezed,Object? workingVersion = null,Object? patchStatus = freezed,Object? isInstalledAdvanced = freezed,Object? customizeList = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? selectedLanguage = freezed,Object? selectedChannel = null,Object? installedCommunityInputMethodSupportVersion = freezed,Object? communityInputMethodLanguageData = freezed,Object? apiLocalizationData = freezed,Object? workingVersion = null,Object? patchStatus = freezed,Object? isInstalledAdvanced = freezed,Object? customizeList = freezed,}) { return _then(_LocalizationUIState( selectedLanguage: freezed == selectedLanguage ? _self.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable -as String?,installedCommunityInputMethodSupportVersion: freezed == installedCommunityInputMethodSupportVersion ? _self.installedCommunityInputMethodSupportVersion : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable +as String?,selectedChannel: null == selectedChannel ? _self.selectedChannel : selectedChannel // ignore: cast_nullable_to_non_nullable +as String,installedCommunityInputMethodSupportVersion: freezed == installedCommunityInputMethodSupportVersion ? _self.installedCommunityInputMethodSupportVersion : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable as String?,communityInputMethodLanguageData: freezed == communityInputMethodLanguageData ? _self.communityInputMethodLanguageData : communityInputMethodLanguageData // ignore: cast_nullable_to_non_nullable as InputMethodApiLanguageData?,apiLocalizationData: freezed == apiLocalizationData ? _self._apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable as Map?,workingVersion: null == workingVersion ? _self.workingVersion : workingVersion // ignore: cast_nullable_to_non_nullable diff --git a/lib/ui/home/localization/localization_ui_model.g.dart b/lib/ui/home/localization/localization_ui_model.g.dart index 88a4385..dadcbea 100644 --- a/lib/ui/home/localization/localization_ui_model.g.dart +++ b/lib/ui/home/localization/localization_ui_model.g.dart @@ -42,7 +42,7 @@ final class LocalizationUIModelProvider } String _$localizationUIModelHash() => - r'd3797a7ff3d31dd1d4b05aed4a9969f4be6853c5'; + r'93c4172a4048a083e941a9011067dc8b17c33ac5'; abstract class _$LocalizationUIModel extends $Notifier { LocalizationUIState build(); diff --git a/lib/ui/home/performance/performance_ui.dart b/lib/ui/home/performance/performance_ui.dart index 381165a..a4f14a4 100644 --- a/lib/ui/home/performance/performance_ui.dart +++ b/lib/ui/home/performance/performance_ui.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; @@ -30,12 +31,8 @@ class HomePerformanceUI extends HookConsumerWidget { children: [ if (state.showGraphicsPerformanceTip) InfoBar( - title: Text(S.current - .performance_info_graphic_optimization_hint), - content: Text( - S.current - .performance_info_graphic_optimization_warning, - ), + title: Text(S.current.performance_info_graphic_optimization_hint), + content: Text(S.current.performance_info_graphic_optimization_warning), onClose: () => model.closeTip(), ), const SizedBox(height: 16), @@ -43,65 +40,74 @@ class HomePerformanceUI extends HookConsumerWidget { children: [ Text( S.current.performance_info_current_status( - state.enabled - ? S.current.performance_info_applied - : S.current.performance_info_not_applied), + state.enabled + ? S.current.performance_info_applied + : S.current.performance_info_not_applied, + ), style: const TextStyle(fontSize: 18), ), const SizedBox(width: 32), - Text( - S.current.performance_action_preset, - style: const TextStyle(fontSize: 18), - ), + Text(S.current.performance_action_preset, style: const TextStyle(fontSize: 18)), for (final item in { "low": S.current.performance_action_low, "medium": S.current.performance_action_medium, "high": S.current.performance_action_high, - "ultra": S.current.performance_action_super + "ultra": S.current.performance_action_super, }.entries) Padding( padding: const EdgeInsets.only(left: 6, right: 6), child: Button( - child: Padding( - padding: const EdgeInsets.only( - top: 2, bottom: 2, left: 4, right: 4), - child: Text(item.value), - ), - onPressed: () => - model.onChangePreProfile(item.key)), + child: Padding( + padding: const EdgeInsets.only(top: 2, bottom: 2, left: 4, right: 4), + child: Text(item.value), + ), + onPressed: () => model.onChangePreProfile(item.key), + ), ), - Text(S.current - .performance_action_info_preset_only_changes_graphics), + Text(S.current.performance_action_info_preset_only_changes_graphics), const Spacer(), Button( onPressed: () => model.refresh(), - child: const Padding( - padding: EdgeInsets.all(6), - child: Icon(FluentIcons.refresh), - ), + child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.refresh)), ), const SizedBox(width: 12), - Button( + if (!kIsWeb) + Button( child: Text( S.current.performance_action_reset_to_default, style: const TextStyle(fontSize: 16), ), - onPressed: () => model.clean(context)), - const SizedBox(width: 24), - Button( + onPressed: () => model.clean(context), + ), + if (!kIsWeb) const SizedBox(width: 24), + if (kIsWeb) + // Web 平台:下载配置文件 + Button( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(FluentIcons.download, size: 16), + const SizedBox(width: 6), + Text("下载配置", style: const TextStyle(fontSize: 16)), + ], + ), + onPressed: () => model.downloadConfigForWeb(), + ) + else ...[ + // 桌面平台:应用配置 + Button( + child: Text(S.current.performance_action_apply, style: const TextStyle(fontSize: 16)), + onPressed: () => model.applyProfile(false), + ), + const SizedBox(width: 6), + Button( child: Text( - S.current.performance_action_apply, + S.current.performance_action_apply_and_clear_shaders, style: const TextStyle(fontSize: 16), ), - onPressed: () => model.applyProfile(false)), - const SizedBox(width: 6), - Button( - child: Text( - S.current - .performance_action_apply_and_clear_shaders, - style: const TextStyle(fontSize: 16), - ), - onPressed: () => model.applyProfile(true)), + onPressed: () => model.applyProfile(true), + ), + ], ], ), const SizedBox(height: 16), @@ -109,73 +115,60 @@ class HomePerformanceUI extends HookConsumerWidget { ), ), Expanded( - child: MasonryGridView.count( - crossAxisCount: 2, - mainAxisSpacing: 1, - crossAxisSpacing: 1, - itemCount: state.performanceMap!.length, - itemBuilder: (context, index) { - return makeItemGroup(context, - state.performanceMap!.entries.elementAt(index), model); - }, - )), + child: MasonryGridView.count( + crossAxisCount: 2, + mainAxisSpacing: 1, + crossAxisSpacing: 1, + itemCount: state.performanceMap!.length, + itemBuilder: (context, index) { + return makeItemGroup(context, state.performanceMap!.entries.elementAt(index), model); + }, + ), + ), ], ), ), if (state.workingString.isNotEmpty) Container( - decoration: BoxDecoration( - color: Colors.black.withAlpha(150), - ), + decoration: BoxDecoration(color: Colors.black.withAlpha(150)), child: Center( child: Column( mainAxisSize: MainAxisSize.min, - children: [ - const ProgressRing(), - const SizedBox(height: 12), - Text(state.workingString), - ], + children: [const ProgressRing(), const SizedBox(height: 12), Text(state.workingString)], ), ), - ) + ), ], ); } - return makeDefaultPage(context, - title: - S.current.performance_title_performance_optimization(model.scPath), - useBodyContainer: true, - content: content); + return makeDefaultPage( + context, + title: S.current.performance_title_performance_optimization(model.scPath), + useBodyContainer: true, + content: content, + ); } Widget makeItemGroup( - BuildContext context, - MapEntry> group, - HomePerformanceUIModel model) { + BuildContext context, + MapEntry> group, + HomePerformanceUIModel model, + ) { return Padding( padding: const EdgeInsets.all(12), child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: FluentTheme.of(context).cardColor, - ), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: FluentTheme.of(context).cardColor), child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "${group.key}", - style: const TextStyle(fontSize: 20), - ), + Text("${group.key}", style: const TextStyle(fontSize: 20)), const SizedBox(height: 6), - Container( - color: - FluentTheme.of(context).cardColor.withValues(alpha: .2), - height: 1), + Container(color: FluentTheme.of(context).cardColor.withValues(alpha: .2), height: 1), const SizedBox(height: 6), - for (final item in group.value) makeItem(context, item, model) + for (final item in group.value) makeItem(context, item, model), ], ), ), @@ -183,17 +176,13 @@ class HomePerformanceUI extends HookConsumerWidget { ); } - Widget makeItem(BuildContext context, GamePerformanceData item, - HomePerformanceUIModel model) { + Widget makeItem(BuildContext context, GamePerformanceData item, HomePerformanceUIModel model) { return Padding( padding: const EdgeInsets.only(top: 8, bottom: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "${item.name}", - style: const TextStyle(fontSize: 16), - ), + Text("${item.name}", style: const TextStyle(fontSize: 16)), const SizedBox(height: 12), if (item.type == "int") Column( @@ -209,9 +198,7 @@ class HomePerformanceUI extends HookConsumerWidget { dPrint(str); if (str.isEmpty) return; final v = int.tryParse(str); - if (v != null && - v < (item.max ?? 0) && - v >= (item.min ?? 0)) { + if (v != null && v < (item.max ?? 0) && v >= (item.min ?? 0)) { item.value = v; } model.updateState(); @@ -233,9 +220,9 @@ class HomePerformanceUI extends HookConsumerWidget { model.updateState(); }, ), - ) + ), ], - ) + ), ], ) else if (item.type == "bool") @@ -247,7 +234,7 @@ class HomePerformanceUI extends HookConsumerWidget { item.value = value ? 1 : 0; model.updateState(); }, - ) + ), ], ) else if (item.type == "customize") @@ -258,11 +245,7 @@ class HomePerformanceUI extends HookConsumerWidget { ), if (item.info != null && item.info!.isNotEmpty) ...[ const SizedBox(height: 12), - Text( - "${item.info}", - style: TextStyle( - fontSize: 14, color: Colors.white.withValues(alpha: .6)), - ), + Text("${item.info}", style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6))), ], const SizedBox(height: 12), if (item.type != "customize") @@ -270,16 +253,13 @@ class HomePerformanceUI extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ Text( - S.current.performance_info_min_max_values( - item.key ?? "", item.min ?? "", item.max ?? ""), + S.current.performance_info_min_max_values(item.key ?? "", item.min ?? "", item.max ?? ""), style: TextStyle(color: Colors.white.withValues(alpha: .6)), - ) + ), ], ), const SizedBox(height: 6), - Container( - color: FluentTheme.of(context).cardColor.withValues(alpha: .1), - height: 1), + Container(color: FluentTheme.of(context).cardColor.withValues(alpha: .1), height: 1), ], ), ); diff --git a/lib/ui/home/performance/performance_ui_model.dart b/lib/ui/home/performance/performance_ui_model.dart index a5baf8c..cbb7699 100644 --- a/lib/ui/home/performance/performance_ui_model.dart +++ b/lib/ui/home/performance/performance_ui_model.dart @@ -1,7 +1,12 @@ // ignore_for_file: avoid_build_context_in_providers, avoid_public_notifier_properties import 'dart:io'; +import 'dart:js_interop'; +import 'dart:typed_data'; +import 'package:archive/archive.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart' show kIsWeb, compute; +import 'package:web/web.dart' as web; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_ce/hive.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -36,7 +41,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { final List _inAppKeys = []; - late final confFile = File("$scPath\\USER.cfg"); + late final confFile = kIsWeb ? null : File("$scPath\\USER.cfg"); static const _graphicsPerformanceTipVersion = 1; @@ -66,7 +71,7 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { } state = state.copyWith(performanceMap: performanceMap); - if (await confFile.exists()) { + if (!kIsWeb && await confFile!.exists()) { await _readConf(); } else { state = state.copyWith(enabled: false); @@ -78,10 +83,10 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { } Future _readConf() async { - if (state.performanceMap == null) return; + if (state.performanceMap == null || kIsWeb) return; state = state.copyWith(enabled: true); - final confString = await confFile.readAsString(); + final confString = await confFile!.readAsString(); for (var value in confString.split("\n")) { final kv = value.split("="); for (var m in state.performanceMap!.entries) { @@ -151,9 +156,15 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { } Future clean(BuildContext context) async { + if (kIsWeb) { + // Web 平台只需要重置状态 + await _init(); + return; + } + state = state.copyWith(workingString: S.current.performance_info_delete_config_file); - if (await confFile.exists()) { - await confFile.delete(recursive: true); + if (await confFile!.exists()) { + await confFile!.delete(recursive: true); } state = state.copyWith(workingString: S.current.performance_action_clear_shaders); if (!context.mounted) return; @@ -181,6 +192,12 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { } Future applyProfile(bool cleanShader) async { + if (kIsWeb) { + // Web 平台使用下载功能 + await downloadConfigForWeb(); + return; + } + if (state.performanceMap == null) return; AnalyticsApi.touch("performance_apply"); state = state.copyWith(workingString: S.current.performance_info_generate_config_file); @@ -203,11 +220,11 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { } } state = state.copyWith(workingString: S.current.performance_info_write_out_config_file); - if (await confFile.exists()) { - await confFile.delete(); + if (await confFile!.exists()) { + await confFile!.delete(); } - await confFile.create(); - await confFile.writeAsString(conf); + await confFile!.create(); + await confFile!.writeAsString(conf); if (cleanShader) { state = state.copyWith(workingString: S.current.performance_action_clear_shaders); await cleanShaderCache(null); @@ -221,4 +238,70 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel { void updateState() { state = state.copyWith(); } + + /// Web 平台下载配置文件 + Future downloadConfigForWeb() async { + if (!kIsWeb) return; + if (state.performanceMap == null) return; + + AnalyticsApi.touch("performance_download_web"); + state = state.copyWith(workingString: S.current.performance_info_generate_config_file); + + String conf = ""; + for (var v in state.performanceMap!.entries) { + for (var c in v.value) { + if (c.key != "customize") { + conf = "$conf${c.key}=${c.value}\n"; + } + } + } + + if (customizeCtrl.text.trim().isNotEmpty) { + final lines = customizeCtrl.text.split("\n"); + for (var value in lines) { + final sp = value.split("="); + // 忽略无效的配置文件 + if (sp.length == 2) { + conf = "$conf${sp[0].trim()}=${sp[1].trim()}\n"; + } + } + } + + await _generateAndDownloadWebFile(conf); + state = state.copyWith(workingString: ""); + } + + Future _generateAndDownloadWebFile(String confContent) async { + final archive = Archive(); + archive.addFile(ArchiveFile("USER.cfg", confContent.length, confContent.codeUnits)); + + final zip = await compute(_encodeZipFile, archive); + if (zip == null) return; + + final blob = Blob.fromBytes(zip, opt: {"type": "application/zip"}); + final url = web.URL.createObjectURL(blob); + jsDownloadBlobFile(url, "StarCitizen_Performance_Config.zip"); + } + + List? _encodeZipFile(Archive archive) { + final zip = ZipEncoder().encode(archive); + return zip; + } } + +// Web 平台支持 - JS 互操作 +@JS("Blob") +extension type Blob._(JSObject _) implements JSObject { + external factory Blob(JSArray blobParts, JSAny? options); + + factory Blob.fromBytes(List bytes, {Map? opt}) { + final data = Uint8List.fromList(bytes).buffer.toJS; + return Blob([data].toJS, opt?.jsify()); + } + + external JSArrayBuffer? get blobParts; + external JSObject? get options; +} + +@JS() +external void jsDownloadBlobFile(String blobUrl, String filename); diff --git a/lib/ui/home/performance/performance_ui_model.g.dart b/lib/ui/home/performance/performance_ui_model.g.dart index 2733524..70dc3d2 100644 --- a/lib/ui/home/performance/performance_ui_model.g.dart +++ b/lib/ui/home/performance/performance_ui_model.g.dart @@ -42,7 +42,7 @@ final class HomePerformanceUIModelProvider } String _$homePerformanceUIModelHash() => - r'c3c55c0470ef8c8be4915a1878deba332653ecde'; + r'4c5c33fe7d85dc8f6bf0d019c1b870d285d594ff'; abstract class _$HomePerformanceUIModel extends $Notifier { diff --git a/lib/ui/index_ui.dart b/lib/ui/index_ui.dart index 98fe9bb..4b6f37f 100644 --- a/lib/ui/index_ui.dart +++ b/lib/ui/index_ui.dart @@ -1,4 +1,6 @@ +import 'package:extended_image/extended_image.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -24,55 +26,110 @@ class IndexUI extends HookConsumerWidget { // pre init child ref.watch(homeUIModelProvider.select((value) => null)); ref.watch(settingsUIModelProvider.select((value) => null)); - ref.watch(appGlobalModelProvider); + final globalState = ref.watch(appGlobalModelProvider); final curIndex = useState(0); + + // Web 版本使用背景图片布局 + if (kIsWeb) { + return Stack( + children: [ + AnimatedSwitcher( + duration: const Duration(seconds: 3), + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition(opacity: animation, child: child); + }, + child: ExtendedImage.asset( + key: ValueKey(globalState.backgroundImageAssetsPath), + width: double.infinity, + height: double.infinity, + globalState.backgroundImageAssetsPath, + fit: BoxFit.cover, + cacheWidth: null, + cacheHeight: null, + loadStateChanged: (state) { + if (state.extendedImageLoadState == LoadState.completed) { + return state.completedWidget; + } + // 加载中或失败时返回黑色背景,避免白屏闪烁 + return Container(width: double.infinity, height: double.infinity, color: Colors.black); + }, + ), + ), + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: BlurOvalWidget( + child: Container( + constraints: const BoxConstraints(maxWidth: 1440, maxHeight: 920), + child: _buildNavigationView(context, curIndex, globalState), + ), + ), + ), + ), + ], + ); + } + + // Desktop 版本直接使用 NavigationView + return _buildNavigationView(context, curIndex, globalState); + } + + Widget _buildNavigationView(BuildContext context, ValueNotifier curIndex, AppGlobalState appState) { return NavigationView( appBar: NavigationAppBar( - automaticallyImplyLeading: false, - title: () { - return DragToMoveArea( - child: Align( - alignment: AlignmentDirectional.centerStart, - child: Row( - children: [ - Image.asset( - "assets/app_logo_mini.png", - width: 20, - height: 20, - fit: BoxFit.cover, - ), - const SizedBox(width: 12), - Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")), - ], - ), + automaticallyImplyLeading: false, + title: () { + if (kIsWeb) { + // Web 版本简化标题 + return Align( + alignment: AlignmentDirectional.centerStart, + child: Row( + children: [ + Image.asset("assets/app_logo_mini.png", width: 20, height: 20, fit: BoxFit.cover), + const SizedBox(width: 12), + Text("${S.current.app_index_version_info(ConstConf.appVersion, "WEB")} [PREVIEW]"), + ], ), ); - }(), - actions: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - icon: Stack( - children: [ - Padding( - padding: const EdgeInsets.all(6), - child: Icon( - FluentIcons.installation, - size: 22, - color: Colors.white.withValues(alpha: .6), - ), - ), - _makeAria2TaskNumWidget() - ], - ), - onPressed: () => _goDownloader(context), - // onPressed: model.goDownloader + } + return DragToMoveArea( + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Row( + children: [ + Image.asset("assets/app_logo_mini.png", width: 20, height: 20, fit: BoxFit.cover), + const SizedBox(width: 12), + Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")), + ], ), - const SizedBox(width: 24), - const WindowButtons() - ], - )), + ), + ); + }(), + actions: kIsWeb + ? null + : Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + icon: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(6), + child: Icon(FluentIcons.installation, size: 22, color: Colors.white.withValues(alpha: .6)), + ), + _makeAria2TaskNumWidget(), + ], + ), + onPressed: () => _goDownloader(context), + ), + const SizedBox(width: 24), + const WindowButtons(), + ], + ), + ), pane: NavigationPane( key: Key("NavigationPane_${S.current.app_language_code}"), selected: curIndex.value, @@ -86,21 +143,12 @@ class IndexUI extends HookConsumerWidget { } Map get pageMenus => { - FluentIcons.home: ( - S.current.app_index_menu_home, - const HomeUI(), - ), - FluentIcons.toolbox: ( - S.current.app_index_menu_tools, - const ToolsUI(), - ), - FluentIcons.power_apps: ((S.current.nav_title), const NavUI()), - FluentIcons.settings: (S.current.app_index_menu_settings, const SettingsUI()), - FluentIcons.info: ( - S.current.app_index_menu_about, - const AboutUI(), - ), - }; + FluentIcons.home: (S.current.app_index_menu_home, const HomeUI()), + if (!kIsWeb) FluentIcons.toolbox: (S.current.app_index_menu_tools, const ToolsUI()), + FluentIcons.power_apps: ((S.current.nav_title), const NavUI()), + FluentIcons.settings: (S.current.app_index_menu_settings, const SettingsUI()), + FluentIcons.info: (S.current.app_index_menu_about, const AboutUI()), + }; List getNavigationPaneItems(ValueNotifier curIndexState) { // width = 64 @@ -116,10 +164,7 @@ class IndexUI extends HookConsumerWidget { children: [ Icon(kv.key, size: 18), const SizedBox(height: 3), - Text( - kv.value.$1, - style: const TextStyle(fontSize: 11), - ) + Text(kv.value.$1, style: const TextStyle(fontSize: 11)), ], ), ), @@ -144,27 +189,24 @@ class IndexUI extends HookConsumerWidget { return const SizedBox(); } return Positioned( - bottom: 0, - right: 0, - child: Container( - decoration: BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.only(left: 6, right: 6, bottom: 1.5, top: 1.5), - child: Text( - "${aria2cState.aria2TotalTaskNum}", - style: const TextStyle( - fontSize: 8, - color: Colors.white, - ), - ), - )); + bottom: 0, + right: 0, + child: Container( + decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(12)), + padding: const EdgeInsets.only(left: 6, right: 6, bottom: 1.5, top: 1.5), + child: Text("${aria2cState.aria2TotalTaskNum}", style: const TextStyle(fontSize: 8, color: Colors.white)), + ), + ); }, ); } void _goDownloader(BuildContext context) { - context.push('/index/downloader'); + if (kIsWeb) { + // Web 版本提示下载完整版 + showToast(context, "此功能需要下载完整版 SCToolBox"); + return; + } + context.push('/downloader'); } -} \ No newline at end of file +} diff --git a/lib/ui/settings/settings_ui.dart b/lib/ui/settings/settings_ui.dart index 579aca7..bd06f8a 100644 --- a/lib/ui/settings/settings_ui.dart +++ b/lib/ui/settings/settings_ui.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/app.dart'; @@ -14,86 +15,103 @@ class SettingsUI extends HookConsumerWidget { final model = ref.read(settingsUIModelProvider.notifier); final appGlobalState = ref.watch(appGlobalModelProvider); final appGlobalModel = ref.read(appGlobalModelProvider.notifier); - return ListView(padding: const EdgeInsets.all(16), children: [ - makeTitle(S.current.settings_title_general), - makeSettingsItem( - const Icon(FontAwesomeIcons.language, size: 20), - S.current.settings_app_language, - subTitle: S.current.settings_app_language_switch_info, - onTap: () {}, - onComboChanged: appGlobalModel.changeLocale, - comboMenus: AppGlobalModel.appLocaleSupport, - selectedComboValue: appGlobalState.appLocale ?? const Locale("auto"), - showGoIcon: false, - ), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.link, size: 20), - S.current.setting_action_create_settings_shortcut, - subTitle: S.current.setting_action_create_desktop_shortcut, - onTap: () => model.addShortCut(context)), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FontAwesomeIcons.networkWired, size: 20), - S.current.settings_item_dns, - subTitle: S.current.settings_item_dns_info, - switchStatus: sate.isUseInternalDNS, - onSwitch: model.onChangeUseInternalDNS, - onTap: () => model.onChangeUseInternalDNS(!sate.isUseInternalDNS)), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.delete, size: 20), - S.current.setting_action_clear_translation_file_cache, - subTitle: S.current.setting_action_info_cache_clearing_info( - (sate.locationCacheSize / 1024 / 1024).toStringAsFixed(2)), - onTap: () => model.cleanLocationCache(context)), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.speed_high, size: 20), - S.current.setting_action_tool_site_access_acceleration, - onTap: () => - model.onChangeToolSiteMirror(!sate.isEnableToolSiteMirrors), - subTitle: S.current.setting_action_info_mirror_server_info, - onSwitch: model.onChangeToolSiteMirror, - switchStatus: sate.isEnableToolSiteMirrors), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.document_set, size: 20), - S.current.setting_action_view_log, - onTap: () => model.showLogs(), - subTitle: S.current.setting_action_info_view_log_file), - makeTitle(S.current.settings_title_game), - makeSettingsItem(const Icon(FontAwesomeIcons.microchip, size: 20), - S.current.setting_action_ignore_efficiency_cores_on_launch, - subTitle: S.current - .setting_action_set_core_count(sate.inputGameLaunchECore), - onTap: () => model.setGameLaunchECore(context)), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.folder_open, size: 20), - S.current.setting_action_set_launcher_file, - subTitle: sate.customLauncherPath != null - ? "${sate.customLauncherPath}" - : S.current.setting_action_info_manual_launcher_location_setting, - onTap: () => model.setLauncherPath(context), - onDel: () { - model.delName("custom_launcher_path"); - }), - const SizedBox(height: 12), - makeSettingsItem(const Icon(FluentIcons.game, size: 20), - S.current.setting_action_set_game_file, - subTitle: sate.customGamePath != null - ? "${sate.customGamePath}" - : S.current.setting_action_info_manual_game_location_setting, - onTap: () => model.setGamePath(context), - onDel: () { - model.delName("custom_game_path"); - }), - const SizedBox(height: 12), - ]); + return ListView( + padding: const EdgeInsets.all(16), + children: [ + makeTitle(S.current.settings_title_general), + makeSettingsItem( + const Icon(FontAwesomeIcons.language, size: 20), + S.current.settings_app_language, + subTitle: S.current.settings_app_language_switch_info, + onTap: () {}, + onComboChanged: appGlobalModel.changeLocale, + comboMenus: AppGlobalModel.appLocaleSupport, + selectedComboValue: appGlobalState.appLocale ?? const Locale("auto"), + showGoIcon: false, + ), + if (!kIsWeb) ...[ + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.link, size: 20), + S.current.setting_action_create_settings_shortcut, + subTitle: S.current.setting_action_create_desktop_shortcut, + onTap: () => model.addShortCut(context), + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FontAwesomeIcons.networkWired, size: 20), + S.current.settings_item_dns, + subTitle: S.current.settings_item_dns_info, + switchStatus: sate.isUseInternalDNS, + onSwitch: model.onChangeUseInternalDNS, + onTap: () => model.onChangeUseInternalDNS(!sate.isUseInternalDNS), + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.delete, size: 20), + S.current.setting_action_clear_translation_file_cache, + subTitle: S.current.setting_action_info_cache_clearing_info( + (sate.locationCacheSize / 1024 / 1024).toStringAsFixed(2), + ), + onTap: () => model.cleanLocationCache(context), + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.speed_high, size: 20), + S.current.setting_action_tool_site_access_acceleration, + onTap: () => model.onChangeToolSiteMirror(!sate.isEnableToolSiteMirrors), + subTitle: S.current.setting_action_info_mirror_server_info, + onSwitch: model.onChangeToolSiteMirror, + switchStatus: sate.isEnableToolSiteMirrors, + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.document_set, size: 20), + S.current.setting_action_view_log, + onTap: () => model.showLogs(), + subTitle: S.current.setting_action_info_view_log_file, + ), + makeTitle(S.current.settings_title_game), + makeSettingsItem( + const Icon(FontAwesomeIcons.microchip, size: 20), + S.current.setting_action_ignore_efficiency_cores_on_launch, + subTitle: S.current.setting_action_set_core_count(sate.inputGameLaunchECore), + onTap: () => model.setGameLaunchECore(context), + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.folder_open, size: 20), + S.current.setting_action_set_launcher_file, + subTitle: sate.customLauncherPath != null + ? "${sate.customLauncherPath}" + : S.current.setting_action_info_manual_launcher_location_setting, + onTap: () => model.setLauncherPath(context), + onDel: () { + model.delName("custom_launcher_path"); + }, + ), + const SizedBox(height: 12), + makeSettingsItem( + const Icon(FluentIcons.game, size: 20), + S.current.setting_action_set_game_file, + subTitle: sate.customGamePath != null + ? "${sate.customGamePath}" + : S.current.setting_action_info_manual_game_location_setting, + onTap: () => model.setGamePath(context), + onDel: () { + model.delName("custom_game_path"); + }, + ), + const SizedBox(height: 12), + ], + ], + ); } Widget makeTitle(String title) { return Padding( padding: const EdgeInsets.only(top: 12, bottom: 12), - child: Text( - title, - style: TextStyle(fontSize: 24), - ), + child: Text(title, style: TextStyle(fontSize: 24)), ); } @@ -122,12 +140,7 @@ class SettingsUI extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text(title), - const Spacer(), - ], - ), + Row(children: [Text(title), const Spacer()]), if (subTitle != null) ...[ const SizedBox(height: 3), Padding( @@ -135,41 +148,29 @@ class SettingsUI extends HookConsumerWidget { child: Text( subTitle, textAlign: TextAlign.start, - style: TextStyle( - fontSize: 12, - color: Colors.white.withValues(alpha: .6)), + style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .6)), ), ), - ] + ], ], ), ), if (onDel != null) ...[ Button( - onPressed: onDel, - child: const Padding( - padding: EdgeInsets.all(6), - child: Icon(FluentIcons.delete), - )), - ], - if (onSwitch != null) ...[ - ToggleSwitch(checked: switchStatus, onChanged: onSwitch), + onPressed: onDel, + child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.delete)), + ), ], + if (onSwitch != null) ...[ToggleSwitch(checked: switchStatus, onChanged: onSwitch)], if (comboMenus.isNotEmpty) ...[ SizedBox( height: 36, child: ComboBox( value: selectedComboValue, - items: [ - for (final mkv in comboMenus.entries) - ComboBoxItem( - value: mkv.key, - child: Text(mkv.value), - ) - ], + items: [for (final mkv in comboMenus.entries) ComboBoxItem(value: mkv.key, child: Text(mkv.value))], onChanged: onComboChanged, ), - ) + ), ], const SizedBox(width: 12), if (showGoIcon) const Icon(FluentIcons.chevron_right), diff --git a/lib/ui/settings/settings_ui_model.g.dart b/lib/ui/settings/settings_ui_model.g.dart index 82f5ddf..4fd2682 100644 --- a/lib/ui/settings/settings_ui_model.g.dart +++ b/lib/ui/settings/settings_ui_model.g.dart @@ -41,7 +41,7 @@ final class SettingsUIModelProvider } } -String _$settingsUIModelHash() => r'5c08c56bf5464ef44bee8edb8c18c08d4217f135'; +String _$settingsUIModelHash() => r'd19104d924f018a9230548d0372692fc344adacd'; abstract class _$SettingsUIModel extends $Notifier { SettingsUIState build(); diff --git a/lib/ui/splash_ui.dart b/lib/ui/splash_ui.dart index e88c0f6..dc4738b 100644 --- a/lib/ui/splash_ui.dart +++ b/lib/ui/splash_ui.dart @@ -9,7 +9,6 @@ import 'package:markdown_widget/widget/markdown.dart'; import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/app.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; -import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/provider/aria2c.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; @@ -30,42 +29,37 @@ class SplashUI extends HookConsumerWidget { return null; }, []); - return makeDefaultPage(context, - content: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset("assets/app_logo.png", width: 192, height: 192), - const SizedBox(height: 32), - const ProgressRing(), - const SizedBox(height: 32), - if (step == 0) Text(S.current.app_splash_checking_availability), - if (step == 1) Text(S.current.app_splash_checking_for_updates), - if (step == 2) Text(S.current.app_splash_almost_done), - ], - ), + return makeDefaultPage( + context, + content: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset("assets/app_logo.png", width: 192, height: 192), + const SizedBox(height: 32), + const ProgressRing(), + const SizedBox(height: 32), + if (step == 0) Text(S.current.app_splash_checking_availability), + if (step == 1) Text(S.current.app_splash_checking_for_updates), + if (step == 2) Text(S.current.app_splash_almost_done), + ], ), - automaticallyImplyLeading: false, - titleRow: Align( - alignment: AlignmentDirectional.centerStart, - child: Row( - children: [ - Image.asset( - "assets/app_logo_mini.png", - width: 20, - height: 20, - fit: BoxFit.cover, - ), - const SizedBox(width: 12), - Text(S.current.app_index_version_info( - ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")) - ], - ), - )); + ), + automaticallyImplyLeading: false, + titleRow: Align( + alignment: AlignmentDirectional.centerStart, + child: Row( + children: [ + Image.asset("assets/app_logo_mini.png", width: 20, height: 20, fit: BoxFit.cover), + const SizedBox(width: 12), + Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")), + ], + ), + ), + ); } - void _initApp(BuildContext context, AppGlobalModel appModel, - ValueNotifier stepState, WidgetRef ref) async { + void _initApp(BuildContext context, AppGlobalModel appModel, ValueNotifier stepState, WidgetRef ref) async { await appModel.initApp(); final appConf = await Hive.openBox("app_conf"); final v = appConf.get("splash_alert_info_version", defaultValue: 0); @@ -74,11 +68,11 @@ class SplashUI extends HookConsumerWidget { if (!context.mounted) return; await _showAlert(context, appConf); } - try { - await URLConf.checkHost(); - } catch (e) { - dPrint("checkHost Error:$e"); - } + // try { + // await URLConf.checkHost(); + // } catch (e) { + // dPrint("checkHost Error:$e"); + // } stepState.value = 1; if (!context.mounted) return; dPrint("_initApp checkUpdate"); @@ -87,16 +81,16 @@ class SplashUI extends HookConsumerWidget { dPrint("_initApp aria2cModelProvider"); ref.read(aria2cModelProvider); if (!context.mounted) return; - context.go("/index"); + context.go("/"); } Future _showAlert(BuildContext context, Box appConf) async { final userOk = await showConfirmDialogs( - context, - S.current.app_splash_dialog_u_a_p_p, - MarkdownWidget(data: S.current.app_splash_dialog_u_a_p_p_content), - constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .5)); + context, + S.current.app_splash_dialog_u_a_p_p, + MarkdownWidget(data: S.current.app_splash_dialog_u_a_p_p_content), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .5), + ); if (userOk) { await appConf.put("splash_alert_info_version", _alertInfoVersion); } else { diff --git a/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.dart b/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.dart index b3f198d..65ef41f 100644 --- a/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.dart +++ b/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.dart @@ -13,7 +13,6 @@ import 'package:starcitizen_doctor/app.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; -import 'package:starcitizen_doctor/common/rust/api/asar_api.dart' as asar_api; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/generated/no_l10n_strings.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; @@ -24,7 +23,7 @@ part 'rsi_launcher_enhance_dialog_ui.freezed.dart'; abstract class RSILauncherStateData with _$RSILauncherStateData { const factory RSILauncherStateData({ required String version, - required asar_api.RsiLauncherAsarData data, + required dynamic data, required String serverData, @Default(false) bool isPatchInstalled, String? enabledLocalization, @@ -70,8 +69,11 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { workingText.value = S.current.tools_rsi_launcher_enhance_working_msg1; if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) { if (!context.mounted) return; - showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + showToast( + context, + S.current.tools_action_info_rsi_launcher_running_warning, + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); workingText.value = ""; return; } @@ -92,16 +94,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { return ContentDialog( constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .48), - title: Row(children: [ - IconButton( - icon: const Icon( - FluentIcons.back, - size: 22, - ), - onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null), - const SizedBox(width: 12), - Text(S.current.tools_rsi_launcher_enhance_title), - ]), + title: Row( + children: [ + IconButton( + icon: const Icon(FluentIcons.back, size: 22), + onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null, + ), + const SizedBox(width: 12), + Text(S.current.tools_rsi_launcher_enhance_title), + ], + ), content: AnimatedSize( duration: const Duration(milliseconds: 130), child: Column( @@ -112,17 +114,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { InfoBar( title: const SizedBox(), content: Text(S.current.home_localization_action_rsi_launcher_no_game_path_msg), - style: InfoBarThemeData(decoration: (severity) { - return BoxDecoration( - color: Colors.orange, - ); - }, iconColor: (severity) { - return Colors.white; - }), - ), - const SizedBox( - height: 12, + style: InfoBarThemeData( + decoration: (severity) { + return BoxDecoration(color: Colors.orange); + }, + iconColor: (severity) { + return Colors.white; + }, + ), ), + const SizedBox(height: 12), ], if (workingText.value.isNotEmpty) ...[ Center( @@ -144,19 +145,17 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { Expanded( child: Text( S.current.tools_rsi_launcher_enhance_msg_version(assarState.value?.version ?? ""), - style: TextStyle( - color: Colors.white.withValues(alpha: .6), - ), + style: TextStyle(color: Colors.white.withValues(alpha: .6)), ), ), Text( - S.current.tools_rsi_launcher_enhance_msg_patch_status((assarState.value?.isPatchInstalled ?? false) - ? S.current.localization_info_installed - : S.current.tools_action_info_not_installed), - style: TextStyle( - color: Colors.white.withValues(alpha: .6), + S.current.tools_rsi_launcher_enhance_msg_patch_status( + (assarState.value?.isPatchInstalled ?? false) + ? S.current.localization_info_installed + : S.current.tools_action_info_not_installed, ), - ) + style: TextStyle(color: Colors.white.withValues(alpha: .6)), + ), ], ), if (assarState.value?.serverData.isEmpty ?? true) ...[ @@ -165,6 +164,84 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { const SizedBox(height: 24), if (assarState.value?.enabledLocalization != null) Container( + padding: const EdgeInsets.all(12), + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: FluentTheme.of(context).cardColor, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(S.current.tools_rsi_launcher_enhance_title_localization), + const SizedBox(height: 3), + Text( + S.current.tools_rsi_launcher_enhance_subtitle_localization, + style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)), + ), + ], + ), + ), + ComboBox( + items: [ + for (final key in supportLocalizationMap.keys) + ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!)), + ], + value: assarState.value?.enabledLocalization, + onChanged: (v) { + assarState.value = assarState.value!.copyWith(enabledLocalization: v); + }, + ), + ], + ), + ), + const SizedBox(height: 3), + if (assarState.value?.enableDownloaderBoost != null) ...[ + IconButton( + icon: Padding( + padding: const EdgeInsets.only(top: 3, bottom: 3), + child: Row( + children: [ + Expanded( + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down), + const SizedBox(width: 12), + Text( + expandEnhance.value + ? S.current.tools_rsi_launcher_enhance_action_fold + : S.current.tools_rsi_launcher_enhance_action_expand, + ), + ], + ), + ), + ), + ], + ), + ), + onPressed: () async { + if (!expandEnhance.value) { + final userOK = await showConfirmDialogs( + context, + S.current.tools_rsi_launcher_enhance_note_title, + Column( + mainAxisSize: MainAxisSize.min, + children: [Text(S.current.tools_rsi_launcher_enhance_note_msg)], + ), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55), + ); + if (!userOK) return; + } + expandEnhance.value = !expandEnhance.value; + }, + ), + if (expandEnhance.value) + Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( @@ -174,111 +251,38 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { child: Row( children: [ Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(S.current.tools_rsi_launcher_enhance_title_localization), - const SizedBox(height: 3), - Text( - S.current.tools_rsi_launcher_enhance_subtitle_localization, - style: TextStyle( - fontSize: 13, - color: Colors.white.withValues(alpha: .6), - ), - ), - ], - )), - ComboBox( - items: [ - for (final key in supportLocalizationMap.keys) - ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!)) - ], - value: assarState.value?.enabledLocalization, - onChanged: (v) { - assarState.value = assarState.value!.copyWith(enabledLocalization: v); - }, - ), - ], - )), - const SizedBox(height: 3), - if (assarState.value?.enableDownloaderBoost != null) ...[ - IconButton( - icon: Padding( - padding: const EdgeInsets.only(top: 3, bottom: 3), - child: Row( - children: [ - Expanded( - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down), - const SizedBox(width: 12), - Text(expandEnhance.value - ? S.current.tools_rsi_launcher_enhance_action_fold - : S.current.tools_rsi_launcher_enhance_action_expand), - ], - ))), - ], - ), - ), - onPressed: () async { - if (!expandEnhance.value) { - final userOK = await showConfirmDialogs( - context, - S.current.tools_rsi_launcher_enhance_note_title, - Column( - mainAxisSize: MainAxisSize.min, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(S.current.tools_rsi_launcher_enhance_note_msg), + Text(S.current.tools_rsi_launcher_enhance_title_download_booster), + const SizedBox(height: 3), + Text( + S.current.tools_rsi_launcher_enhance_subtitle_download_booster, + style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)), + ), ], ), - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55)); - if (!userOK) return; - } - expandEnhance.value = !expandEnhance.value; - }, - ), - if (expandEnhance.value) - Container( - padding: const EdgeInsets.all(12), - margin: const EdgeInsets.only(bottom: 12), - decoration: BoxDecoration( - color: FluentTheme.of(context).cardColor, - borderRadius: BorderRadius.circular(12), - ), - child: Row(children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(S.current.tools_rsi_launcher_enhance_title_download_booster), - const SizedBox(height: 3), - Text( - S.current.tools_rsi_launcher_enhance_subtitle_download_booster, - style: TextStyle( - fontSize: 13, - color: Colors.white.withValues(alpha: .6), - ), - ), - ], - )), + ), ToggleSwitch( onChanged: (value) { assarState.value = assarState.value?.copyWith(enableDownloaderBoost: value); }, checked: assarState.value?.enableDownloaderBoost ?? false, - ) - ])), + ), + ], + ), + ), ], const SizedBox(height: 12), Center( - child: FilledButton( - onPressed: doInstall, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), - child: Text(S.current.tools_rsi_launcher_enhance_action_install), - ))), + child: FilledButton( + onPressed: doInstall, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + child: Text(S.current.tools_rsi_launcher_enhance_action_install), + ), + ), + ), ], const SizedBox(height: 16), Text( @@ -303,37 +307,41 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { final dataPath = "${lPath}resources\\app.asar"; dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherDataPath ==== $dataPath"); try { - final data = await asar_api.getRsiLauncherAsarData(asarPath: dataPath); - dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath main.js path == ${data.mainJsPath}"); - final version = RegExp(r"main\.(\w+)\.js").firstMatch(data.mainJsPath)?.group(1); - if (version == null) { - if (!context.mounted) return null; - showToast(context, S.current.tools_rsi_launcher_enhance_msg_error_get_launcher_info_error); - return null; - } - dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath main.js version == $version"); + // final data = await asar_api.getRsiLauncherAsarData(asarPath: dataPath); + // dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath main.js path == ${data.mainJsPath}"); + // final version = RegExp(r"main\.(\w+)\.js").firstMatch(data.mainJsPath)?.group(1); + // if (version == null) { + // if (!context.mounted) return null; + // showToast(context, S.current.tools_rsi_launcher_enhance_msg_error_get_launcher_info_error); + // return null; + // } + // dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath main.js version == $version"); - final mainJsString = String.fromCharCodes(data.mainJsContent); + // final mainJsString = String.fromCharCodes(data.mainJsContent); - final (enabledLocalization, enableDownloaderBoost) = _readScriptState(mainJsString); + // final (enabledLocalization, enableDownloaderBoost) = _readScriptState(mainJsString); - return RSILauncherStateData( - version: version, - data: data, - serverData: "", - isPatchInstalled: mainJsString.contains("SC_TOOLBOX"), - enabledLocalization: enabledLocalization, - enableDownloaderBoost: enableDownloaderBoost, - ); + // return RSILauncherStateData( + // version: version, + // data: data, + // serverData: "", + // isPatchInstalled: mainJsString.contains("SC_TOOLBOX"), + // enabledLocalization: enabledLocalization, + // enableDownloaderBoost: enableDownloaderBoost, + // ); } catch (e) { if (!context.mounted) return null; showToast(context, S.current.tools_rsi_launcher_enhance_msg_error_get_launcher_info_error_with_args(e)); return null; } + return null; } Future _loadEnhanceData( - BuildContext context, WidgetRef ref, ValueNotifier assarState) async { + BuildContext context, + WidgetRef ref, + ValueNotifier assarState, + ) async { final globalModel = ref.read(appGlobalModelProvider); final enhancePath = "${globalModel.applicationSupportDir}/launcher_enhance_data"; @@ -418,11 +426,13 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget { for (final line in serverScriptLines) { final lineTrim = line.trim(); if (lineTrim.startsWith(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START)) { - scriptBuffer - .writeln("$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";"); + scriptBuffer.writeln( + "$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";", + ); } else if (lineTrim.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) { - scriptBuffer - .writeln("$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};"); + scriptBuffer.writeln( + "$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};", + ); } else { scriptBuffer.writeln(line); } diff --git a/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.freezed.dart b/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.freezed.dart index 44d11a5..a77b75e 100644 --- a/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.freezed.dart +++ b/lib/ui/tools/dialogs/rsi_launcher_enhance_dialog_ui.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$RSILauncherStateData { - String get version; asar_api.RsiLauncherAsarData get data; String get serverData; bool get isPatchInstalled; String? get enabledLocalization; bool? get enableDownloaderBoost; + String get version; dynamic get data; String get serverData; bool get isPatchInstalled; String? get enabledLocalization; bool? get enableDownloaderBoost; /// Create a copy of RSILauncherStateData /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,12 +25,12 @@ $RSILauncherStateDataCopyWith get copyWith => _$RSILaunche @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is RSILauncherStateData&&(identical(other.version, version) || other.version == version)&&(identical(other.data, data) || other.data == data)&&(identical(other.serverData, serverData) || other.serverData == serverData)&&(identical(other.isPatchInstalled, isPatchInstalled) || other.isPatchInstalled == isPatchInstalled)&&(identical(other.enabledLocalization, enabledLocalization) || other.enabledLocalization == enabledLocalization)&&(identical(other.enableDownloaderBoost, enableDownloaderBoost) || other.enableDownloaderBoost == enableDownloaderBoost)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is RSILauncherStateData&&(identical(other.version, version) || other.version == version)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.serverData, serverData) || other.serverData == serverData)&&(identical(other.isPatchInstalled, isPatchInstalled) || other.isPatchInstalled == isPatchInstalled)&&(identical(other.enabledLocalization, enabledLocalization) || other.enabledLocalization == enabledLocalization)&&(identical(other.enableDownloaderBoost, enableDownloaderBoost) || other.enableDownloaderBoost == enableDownloaderBoost)); } @override -int get hashCode => Object.hash(runtimeType,version,data,serverData,isPatchInstalled,enabledLocalization,enableDownloaderBoost); +int get hashCode => Object.hash(runtimeType,version,const DeepCollectionEquality().hash(data),serverData,isPatchInstalled,enabledLocalization,enableDownloaderBoost); @override String toString() { @@ -45,7 +45,7 @@ abstract mixin class $RSILauncherStateDataCopyWith<$Res> { factory $RSILauncherStateDataCopyWith(RSILauncherStateData value, $Res Function(RSILauncherStateData) _then) = _$RSILauncherStateDataCopyWithImpl; @useResult $Res call({ - String version, asar_api.RsiLauncherAsarData data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost + String version, dynamic data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost }); @@ -62,11 +62,11 @@ class _$RSILauncherStateDataCopyWithImpl<$Res> /// Create a copy of RSILauncherStateData /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? version = null,Object? data = null,Object? serverData = null,Object? isPatchInstalled = null,Object? enabledLocalization = freezed,Object? enableDownloaderBoost = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? version = null,Object? data = freezed,Object? serverData = null,Object? isPatchInstalled = null,Object? enabledLocalization = freezed,Object? enableDownloaderBoost = freezed,}) { return _then(_self.copyWith( version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable -as String,data: null == data ? _self.data : data // ignore: cast_nullable_to_non_nullable -as asar_api.RsiLauncherAsarData,serverData: null == serverData ? _self.serverData : serverData // ignore: cast_nullable_to_non_nullable +as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as dynamic,serverData: null == serverData ? _self.serverData : serverData // ignore: cast_nullable_to_non_nullable as String,isPatchInstalled: null == isPatchInstalled ? _self.isPatchInstalled : isPatchInstalled // ignore: cast_nullable_to_non_nullable as bool,enabledLocalization: freezed == enabledLocalization ? _self.enabledLocalization : enabledLocalization // ignore: cast_nullable_to_non_nullable as String?,enableDownloaderBoost: freezed == enableDownloaderBoost ? _self.enableDownloaderBoost : enableDownloaderBoost // ignore: cast_nullable_to_non_nullable @@ -155,7 +155,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String version, asar_api.RsiLauncherAsarData data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String version, dynamic data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _RSILauncherStateData() when $default != null: return $default(_that.version,_that.data,_that.serverData,_that.isPatchInstalled,_that.enabledLocalization,_that.enableDownloaderBoost);case _: @@ -176,7 +176,7 @@ return $default(_that.version,_that.data,_that.serverData,_that.isPatchInstalled /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String version, asar_api.RsiLauncherAsarData data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String version, dynamic data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost) $default,) {final _that = this; switch (_that) { case _RSILauncherStateData(): return $default(_that.version,_that.data,_that.serverData,_that.isPatchInstalled,_that.enabledLocalization,_that.enableDownloaderBoost);case _: @@ -196,7 +196,7 @@ return $default(_that.version,_that.data,_that.serverData,_that.isPatchInstalled /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String version, asar_api.RsiLauncherAsarData data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String version, dynamic data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost)? $default,) {final _that = this; switch (_that) { case _RSILauncherStateData() when $default != null: return $default(_that.version,_that.data,_that.serverData,_that.isPatchInstalled,_that.enabledLocalization,_that.enableDownloaderBoost);case _: @@ -215,7 +215,7 @@ class _RSILauncherStateData implements RSILauncherStateData { @override final String version; -@override final asar_api.RsiLauncherAsarData data; +@override final dynamic data; @override final String serverData; @override@JsonKey() final bool isPatchInstalled; @override final String? enabledLocalization; @@ -231,12 +231,12 @@ _$RSILauncherStateDataCopyWith<_RSILauncherStateData> get copyWith => __$RSILaun @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _RSILauncherStateData&&(identical(other.version, version) || other.version == version)&&(identical(other.data, data) || other.data == data)&&(identical(other.serverData, serverData) || other.serverData == serverData)&&(identical(other.isPatchInstalled, isPatchInstalled) || other.isPatchInstalled == isPatchInstalled)&&(identical(other.enabledLocalization, enabledLocalization) || other.enabledLocalization == enabledLocalization)&&(identical(other.enableDownloaderBoost, enableDownloaderBoost) || other.enableDownloaderBoost == enableDownloaderBoost)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RSILauncherStateData&&(identical(other.version, version) || other.version == version)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.serverData, serverData) || other.serverData == serverData)&&(identical(other.isPatchInstalled, isPatchInstalled) || other.isPatchInstalled == isPatchInstalled)&&(identical(other.enabledLocalization, enabledLocalization) || other.enabledLocalization == enabledLocalization)&&(identical(other.enableDownloaderBoost, enableDownloaderBoost) || other.enableDownloaderBoost == enableDownloaderBoost)); } @override -int get hashCode => Object.hash(runtimeType,version,data,serverData,isPatchInstalled,enabledLocalization,enableDownloaderBoost); +int get hashCode => Object.hash(runtimeType,version,const DeepCollectionEquality().hash(data),serverData,isPatchInstalled,enabledLocalization,enableDownloaderBoost); @override String toString() { @@ -251,7 +251,7 @@ abstract mixin class _$RSILauncherStateDataCopyWith<$Res> implements $RSILaunche factory _$RSILauncherStateDataCopyWith(_RSILauncherStateData value, $Res Function(_RSILauncherStateData) _then) = __$RSILauncherStateDataCopyWithImpl; @override @useResult $Res call({ - String version, asar_api.RsiLauncherAsarData data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost + String version, dynamic data, String serverData, bool isPatchInstalled, String? enabledLocalization, bool? enableDownloaderBoost }); @@ -268,11 +268,11 @@ class __$RSILauncherStateDataCopyWithImpl<$Res> /// Create a copy of RSILauncherStateData /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? version = null,Object? data = null,Object? serverData = null,Object? isPatchInstalled = null,Object? enabledLocalization = freezed,Object? enableDownloaderBoost = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? version = null,Object? data = freezed,Object? serverData = null,Object? isPatchInstalled = null,Object? enabledLocalization = freezed,Object? enableDownloaderBoost = freezed,}) { return _then(_RSILauncherStateData( version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable -as String,data: null == data ? _self.data : data // ignore: cast_nullable_to_non_nullable -as asar_api.RsiLauncherAsarData,serverData: null == serverData ? _self.serverData : serverData // ignore: cast_nullable_to_non_nullable +as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as dynamic,serverData: null == serverData ? _self.serverData : serverData // ignore: cast_nullable_to_non_nullable as String,isPatchInstalled: null == isPatchInstalled ? _self.isPatchInstalled : isPatchInstalled // ignore: cast_nullable_to_non_nullable as bool,enabledLocalization: freezed == enabledLocalization ? _self.enabledLocalization : enabledLocalization // ignore: cast_nullable_to_non_nullable as String?,enableDownloaderBoost: freezed == enableDownloaderBoost ? _self.enableDownloaderBoost : enableDownloaderBoost // ignore: cast_nullable_to_non_nullable diff --git a/lib/ui/tools/tools_ui_model.dart b/lib/ui/tools/tools_ui_model.dart index e9eb890..72b43e0 100644 --- a/lib/ui/tools/tools_ui_model.dart +++ b/lib/ui/tools/tools_ui_model.dart @@ -67,30 +67,30 @@ class ToolsUIModel extends _$ToolsUIModel { if (state.isItemLoading) return; var items = []; state = state.copyWith(items: items, isItemLoading: true); - if (!skipPathScan) { - await reScanPath(context); - } + // if (!skipPathScan) { + // await reScanPath(context); + // } try { items = [ - ToolsItemData( - "systemnfo", - S.current.tools_action_view_system_info, - S.current.tools_action_info_view_critical_system_info, - const Icon(FluentIcons.system, size: 24), - onTap: () => _showSystemInfo(context), - ), + // ToolsItemData( + // "systemnfo", + // S.current.tools_action_view_system_info, + // S.current.tools_action_info_view_critical_system_info, + // const Icon(FluentIcons.system, size: 24), + // onTap: () => _showSystemInfo(context), + // ), ]; if (!context.mounted) return; items.add(await _addP4kCard(context)); items.addAll([ - ToolsItemData( - "hosts_booster", - S.current.tools_action_hosts_acceleration_experimental, - S.current.tools_action_info_hosts_acceleration_experimental_tip, - const Icon(FluentIcons.virtual_network, size: 24), - onTap: () => _doHostsBooster(context), - ), + // ToolsItemData( + // "hosts_booster", + // S.current.tools_action_hosts_acceleration_experimental, + // S.current.tools_action_info_hosts_acceleration_experimental_tip, + // const Icon(FluentIcons.virtual_network, size: 24), + // onTap: () => _doHostsBooster(context), + // ), ToolsItemData( "log_analyze", S.current.log_analyzer_title, @@ -98,45 +98,45 @@ class ToolsUIModel extends _$ToolsUIModel { Icon(FluentIcons.analytics_logo), onTap: () => _showLogAnalyze(context), ), - ToolsItemData( - "rsilauncher_enhance_mod", - S.current.tools_rsi_launcher_enhance_title, - S.current.tools_action_rsi_launcher_enhance_info, - const Icon(FluentIcons.c_plus_plus, size: 24), - onTap: () => rsiEnhance(context), - ), - ToolsItemData( - "reinstall_eac", - S.current.tools_action_reinstall_easyanticheat, - S.current.tools_action_info_reinstall_eac, - const Icon(FluentIcons.game, size: 24), - onTap: () => _reinstallEAC(context), - ), - ToolsItemData( - "rsilauncher_admin_mode", - S.current.tools_action_rsi_launcher_admin_mode, - S.current.tools_action_info_run_rsi_as_admin, - const Icon(FluentIcons.admin, size: 24), - onTap: () => _adminRSILauncher(context), - ), - ToolsItemData( - "unp4kc", - S.current.tools_action_unp4k, - S.current.tools_action_unp4k_info, - const Icon(FontAwesomeIcons.fileZipper, size: 24), - onTap: () => _unp4kc(context), - ), + // ToolsItemData( + // "rsilauncher_enhance_mod", + // S.current.tools_rsi_launcher_enhance_title, + // S.current.tools_action_rsi_launcher_enhance_info, + // const Icon(FluentIcons.c_plus_plus, size: 24), + // onTap: () => rsiEnhance(context), + // ), + // ToolsItemData( + // "reinstall_eac", + // S.current.tools_action_reinstall_easyanticheat, + // S.current.tools_action_info_reinstall_eac, + // const Icon(FluentIcons.game, size: 24), + // onTap: () => _reinstallEAC(context), + // ), + // ToolsItemData( + // "rsilauncher_admin_mode", + // S.current.tools_action_rsi_launcher_admin_mode, + // S.current.tools_action_info_run_rsi_as_admin, + // const Icon(FluentIcons.admin, size: 24), + // onTap: () => _adminRSILauncher(context), + // ), + // ToolsItemData( + // "unp4kc", + // S.current.tools_action_unp4k, + // S.current.tools_action_unp4k_info, + // const Icon(FontAwesomeIcons.fileZipper, size: 24), + // onTap: () => _unp4kc(context), + // ), ]); state = state.copyWith(items: items); if (!context.mounted) return; - items.add(await _addShaderCard(context)); - state = state.copyWith(items: items); + // items.add(await _addShaderCard(context)); + // state = state.copyWith(items: items); if (!context.mounted) return; - items.add(await _addPhotographyCard(context)); - state = state.copyWith(items: items); - if (!context.mounted) return; - items.addAll(await _addNvmePatchCard(context)); + // items.add(await _addPhotographyCard(context)); + // state = state.copyWith(items: items); + // if (!context.mounted) return; + // items.addAll(await _addNvmePatchCard(context)); state = state.copyWith(items: items, isItemLoading: false); } catch (e) { if (!context.mounted) return; @@ -176,7 +176,8 @@ class ToolsUIModel extends _$ToolsUIModel { "remove_nvme_settings", S.current.tools_action_remove_nvme_registry_patch, S.current.tools_action_info_nvme_patch_issue( - nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed), + nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed, + ), const Icon(FluentIcons.hard_drive, size: 24), onTap: nvmePatchStatus ? () async { @@ -208,7 +209,7 @@ class ToolsUIModel extends _$ToolsUIModel { state = state.copyWith(working: false); loadToolsCard(context, skipPathScan: true); }, - ) + ), ]; } @@ -266,8 +267,11 @@ class ToolsUIModel extends _$ToolsUIModel { if (listData == null) { return; } - scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData, - checkExists: checkActive, withVersion: AppConf.gameChannels); + scInstallPaths = await SCLoggerHelper.getGameInstallPath( + listData, + checkExists: checkActive, + withVersion: AppConf.gameChannels, + ); if (scInstallPaths.isNotEmpty) { scInstalledPath = scInstallPaths.first; } @@ -337,11 +341,12 @@ class ToolsUIModel extends _$ToolsUIModel { Future getSystemInfo() async { return S.current.tools_action_info_system_info_content( - await SystemHelper.getSystemName(), - await SystemHelper.getCpuName(), - await SystemHelper.getSystemMemorySizeGB(), - await SystemHelper.getGpuInfo(), - await SystemHelper.getDiskInfo()); + await SystemHelper.getSystemName(), + await SystemHelper.getCpuName(), + await SystemHelper.getSystemMemorySizeGB(), + await SystemHelper.getGpuInfo(), + await SystemHelper.getDiskInfo(), + ); } /// 管理员模式运行 RSI 启动器 @@ -365,9 +370,7 @@ class ToolsUIModel extends _$ToolsUIModel { builder: (context) => ContentDialog( title: Text(S.current.tools_action_info_system_info_title), content: Text(systemInfo), - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * .65, - ), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .65), actions: [ FilledButton( child: Padding( @@ -404,8 +407,11 @@ class ToolsUIModel extends _$ToolsUIModel { if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) { if (!context.mounted) return; - showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + showToast( + context, + S.current.tools_action_info_rsi_launcher_running_warning, + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); return; } @@ -440,8 +446,11 @@ class ToolsUIModel extends _$ToolsUIModel { return; } - final userSelect = - await FilePicker.platform.saveFile(initialDirectory: savePath, fileName: fileName, lockParentWindow: true); + final userSelect = await FilePicker.platform.saveFile( + initialDirectory: savePath, + fileName: fileName, + lockParentWindow: true, + ); if (userSelect == null) { state = state.copyWith(working: false); return; @@ -468,7 +477,7 @@ class ToolsUIModel extends _$ToolsUIModel { await aria2c.saveSession(); AnalyticsApi.touch("p4k_download"); if (!context.mounted) return; - context.push("/index/downloader"); + context.push("/downloader"); } catch (e) { state = state.copyWith(working: false); if (!context.mounted) return; @@ -550,16 +559,18 @@ class ToolsUIModel extends _$ToolsUIModel { static Future rsiEnhance(BuildContext context, {bool showNotGameInstallMsg = false}) async { if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) { if (!context.mounted) return; - showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + showToast( + context, + S.current.tools_action_info_rsi_launcher_running_warning, + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); return; } if (!context.mounted) return; showDialog( - context: context, - builder: (BuildContext context) => RsiLauncherEnhanceDialogUI( - showNotGameInstallMsg: showNotGameInstallMsg, - )); + context: context, + builder: (BuildContext context) => RsiLauncherEnhanceDialogUI(showNotGameInstallMsg: showNotGameInstallMsg), + ); } Future _showLogAnalyze(BuildContext context) async { diff --git a/lib/ui/tools/tools_ui_model.g.dart b/lib/ui/tools/tools_ui_model.g.dart index 8ae0dff..11d06d4 100644 --- a/lib/ui/tools/tools_ui_model.g.dart +++ b/lib/ui/tools/tools_ui_model.g.dart @@ -41,7 +41,7 @@ final class ToolsUIModelProvider } } -String _$toolsUIModelHash() => r'81a73aeccf978f7e620681eaf1a3d4182ff48f9e'; +String _$toolsUIModelHash() => r'87f28130882f9c4b0979ed1afbb71986f41ee24d'; abstract class _$ToolsUIModel extends $Notifier { ToolsUIState build(); diff --git a/lib/widgets/src/blur_oval_widget.dart b/lib/widgets/src/blur_oval_widget.dart new file mode 100644 index 0000000..a35b873 --- /dev/null +++ b/lib/widgets/src/blur_oval_widget.dart @@ -0,0 +1,31 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class BlurOvalWidget extends StatelessWidget { + final Widget child; + final double padding; + final Color blurColor; + final BorderRadius borderRadius; + final ImageFilter? imageFilter; + + const BlurOvalWidget({ + super.key, + required this.child, + this.padding = 0, + this.blurColor = Colors.white10, + this.borderRadius = BorderRadius.zero, + this.imageFilter, + }); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: borderRadius, + child: BackdropFilter( + filter: imageFilter ?? ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container(color: blurColor, child: child), + ), + ); + } +} diff --git a/lib/widgets/src/cache_svg_image.dart b/lib/widgets/src/cache_svg_image.dart index 0e4148c..45794c1 100644 --- a/lib/widgets/src/cache_svg_image.dart +++ b/lib/widgets/src/cache_svg_image.dart @@ -1,9 +1,11 @@ -import 'dart:io'; - import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:starcitizen_doctor/common/utils/file_cache_utils.dart'; + +// Only import FileCacheUtils on non-web platforms +import 'package:starcitizen_doctor/common/utils/file_cache_utils.dart' + if (dart.library.html) 'package:starcitizen_doctor/common/utils/file_cache_utils_stub.dart'; class CachedSvgImage extends HookWidget { final String url; @@ -15,50 +17,53 @@ class CachedSvgImage extends HookWidget { @override Widget build(BuildContext context) { - final cachedFile = useState(null); + // For web platform, directly use network loading + if (kIsWeb) { + return SvgPicture.network( + url, + width: width, + height: height, + fit: fit ?? BoxFit.contain, + placeholderBuilder: (context) => SizedBox( + width: width, + height: height, + child: const Center(child: ProgressRing()), + ), + ); + } + + // For desktop/mobile platforms, use file caching + final cachedFile = useState(null); final errorInfo = useState(null); - useEffect( - () { - () async { - try { - cachedFile.value = await FileCacheUtils.getFile(url); - } catch (e) { - debugPrint("Error loading SVG: $e"); - errorInfo.value = "Error loading SVG: $e"; - } - }(); - return null; - }, - [url], - ); + useEffect(() { + () async { + try { + cachedFile.value = await FileCacheUtils.getFile(url); + } catch (e) { + debugPrint("Error loading SVG: $e"); + errorInfo.value = "Error loading SVG: $e"; + } + }(); + return null; + }, [url]); if (errorInfo.value != null) { return SizedBox( width: width, height: height, child: Center( - child: Text( - errorInfo.value!, - style: TextStyle(color: Colors.red), - ), + child: Text(errorInfo.value!, style: TextStyle(color: Colors.red)), ), ); } return cachedFile.value != null - ? SvgPicture.file( - cachedFile.value!, - width: width, - height: height, - fit: fit ?? BoxFit.contain, - ) + ? SvgPicture.file(cachedFile.value!, width: width, height: height, fit: fit ?? BoxFit.contain) : SizedBox( width: width, height: height, - child: Center( - child: ProgressRing(), - ), + child: const Center(child: ProgressRing()), ); } } diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index 5d3b935..304f574 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -18,6 +18,7 @@ export 'src/countdown_time_text.dart'; export 'src/cache_svg_image.dart'; export 'src/grid_item_animator.dart'; export 'src/swiper.dart'; +export 'src/blur_oval_widget.dart'; export '../common/utils/async.dart'; export '../common/utils/base_utils.dart'; diff --git a/pubspec.lock b/pubspec.lock index c23b50d..68484f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -82,14 +82,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" - build_cli_annotations: - dependency: transitive - description: - name: build_cli_annotations - sha256: e563c2e01de8974566a1998410d3f6f03521788160a02503b0b1f1a46c7b3d95 - url: "https://pub.dev" - source: hosted - version: "2.1.1" build_config: dependency: transitive description: @@ -243,7 +235,7 @@ packages: source: hosted version: "1.15.0" cross_file: - dependency: transitive + dependency: "direct main" description: name: cross_file sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" @@ -395,21 +387,13 @@ packages: source: hosted version: "1.3.3" ffi: - dependency: "direct main" + dependency: transitive description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted version: "2.1.4" - ffigen: - dependency: "direct dev" - description: - name: ffigen - sha256: c880c7cda18808b10304ecbd3e3867b29a8da0d58fd2d03080829e3b65bc567c - url: "https://pub.dev" - source: hosted - version: "20.0.0" file: dependency: "direct main" description: @@ -508,14 +492,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - flutter_rust_bridge: - dependency: "direct main" - description: - name: flutter_rust_bridge - sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e" - url: "https://pub.dev" - source: hosted - version: "2.11.1" flutter_staggered_grid_view: dependency: "direct main" description: @@ -726,22 +702,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2+1" - isolate_contactor: - dependency: transitive - description: - name: isolate_contactor - sha256: "6ba8434ceb58238a1389d6365111a3efe7baa1c68a66f4db6d63d351cf6c3a0f" - url: "https://pub.dev" - source: hosted - version: "4.1.0" isolate_manager: - dependency: transitive + dependency: "direct overridden" description: name: isolate_manager - sha256: "22ed0c25f80ec3b5f21e3a55d060f4650afff33f27c2dff34c0f9409d5759ae5" + sha256: "375e35325c964be41336faeaf86641d742a944f10b28d301237b795f1085cbb7" url: "https://pub.dev" source: hosted - version: "4.1.5+1" + version: "6.1.2" js: dependency: transitive description: @@ -1054,14 +1022,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.dev" - source: hosted - version: "3.2.2" re_editor: dependency: "direct main" description: @@ -1126,13 +1086,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - rust_builder: - dependency: "direct main" - description: - path: rust_builder - relative: true - source: path - version: "0.0.1" rxdart: dependency: transitive description: @@ -1546,7 +1499,7 @@ packages: source: hosted version: "1.1.4" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" @@ -1617,14 +1570,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" - yaml_edit: - dependency: transitive - description: - name: yaml_edit - sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5 - url: "https://pub.dev" - source: hosted - version: "2.2.2" sdks: dart: ">=3.9.0 <4.0.0" flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5df54c1..4a6218e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,19 +42,21 @@ dependencies: uuid: ^4.5.1 flutter_tilt: ^3.3.2 card_swiper: ^3.0.1 - ffi: ^2.1.4 - flutter_rust_bridge: ^2.11.1 + # ffi: ^2.1.4 # 移除 Rust 依赖 + # flutter_rust_bridge: ^2.11.1 # 移除 Rust 依赖 freezed_annotation: ^3.1.0 meta: ^1.16.0 hexcolor: ^3.0.1 html: ^0.15.6 fixnum: ^1.1.1 - rust_builder: - path: rust_builder + # rust_builder: # 移除 Rust 依赖 + # path: rust_builder aria2: git: https://github.com/xkeyC/dart_aria2_rpc.git # path: ../../xkeyC/dart_aria2_rpc # path: ../../xkeyC/dart_aria2_rpc + # rust_builder: # Web 版本不需要 Rust + # path: rust_builder intl: any synchronized: ^3.4.0 super_sliver_list: ^0.4.1 @@ -68,10 +70,12 @@ dependencies: path: ^1.9.1 crypto: ^3.0.7 xml: ^6.6.1 + cross_file: ^0.3.4+2 + web: ^1.0.0 dependency_overrides: http: ^1.5.0 intl: ^0.20.2 - + isolate_manager: ^6.1.2 dev_dependencies: flutter_test: sdk: flutter @@ -83,7 +87,7 @@ dev_dependencies: riverpod_generator: ^3.0.3 custom_lint: ^0.8.0 riverpod_lint: ^3.0.3 - ffigen: ^20.0.0 + # ffigen: ^20.0.0 # 移除 Rust 依赖 sct_dev_tools: path: ./packages/sct_dev_tools @@ -94,6 +98,7 @@ flutter: - assets/ - assets/binary/ - assets/countdown/ + - assets/backgrounds/ - assets/web/input_method/ - assets/web/input_method/js/ - assets/web/input_method/style/ diff --git a/test_web_build.sh b/test_web_build.sh new file mode 100755 index 0000000..3346ea6 --- /dev/null +++ b/test_web_build.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +echo "================================" +echo "SCToolBox Web 版本测试脚本" +echo "================================" +echo "" + +# 检查 Flutter +echo "检查 Flutter 环境..." +if ! command -v flutter &> /dev/null; then + echo "❌ Flutter 未安装" + exit 1 +fi +echo "✅ Flutter 已安装" +echo "" + +# 检查依赖 +echo "检查项目依赖..." +cd "$(dirname "$0")" +flutter pub get +echo "✅ 依赖检查完成" +echo "" + +# 运行代码分析 +echo "运行代码分析..." +flutter analyze --no-fatal-infos +if [ $? -eq 0 ]; then + echo "✅ 代码分析通过" +else + echo "⚠️ 代码分析有警告,但可以继续" +fi +echo "" + +# 测试 Web 编译 +echo "测试 Web 编译..." +echo "运行: flutter build web --web-renderer canvaskit --release" +echo "" +flutter build web --web-renderer canvaskit --release + +if [ $? -eq 0 ]; then + echo "" + echo "================================" + echo "✅ Web 版本构建成功!" + echo "================================" + echo "" + echo "输出目录: build/web/" + echo "" + echo "本地测试方法:" + echo "1. 使用 Python: cd build/web && python3 -m http.server 8000" + echo "2. 使用 PHP: cd build/web && php -S localhost:8000" + echo "3. 使用 Node.js: cd build/web && npx serve" + echo "" + echo "然后访问: http://localhost:8000" + echo "" +else + echo "" + echo "================================" + echo "❌ Web 版本构建失败" + echo "================================" + echo "" + echo "请检查上面的错误信息" + exit 1 +fi diff --git a/web/favicon.png b/web/favicon.png index 8aaa46a..3ceb859 100644 Binary files a/web/favicon.png and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png deleted file mode 100644 index b749bfe..0000000 Binary files a/web/icons/Icon-192.png and /dev/null differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48..0000000 Binary files a/web/icons/Icon-512.png and /dev/null differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d7..0000000 Binary files a/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c566..0000000 Binary files a/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/web/index.html b/web/index.html index 73dc7a7..ca03ecd 100644 --- a/web/index.html +++ b/web/index.html @@ -1,5 +1,6 @@ + - - + + - + - starcitizen_doctor + SCToolBox Web Lite + + +
+
+ +

SCToolBox Web Lite

+

Loading...

+
+
+ + - + + \ No newline at end of file