diff --git a/lib/app.freezed.dart b/lib/app.freezed.dart index b239a30..60c9102 100644 --- a/lib/app.freezed.dart +++ b/lib/app.freezed.dart @@ -215,7 +215,7 @@ class _$AppGlobalStateImpl implements _AppGlobalState { this.appLocale, this.appConfBox, this.backgroundImageAssetsPath = - "assets/backgrounds/SC_01_Wallpaper_3840x2160.jpg"}); + "assets/backgrounds/SC_01_Wallpaper_3840x2160.webp"}); @override final String? deviceUUID; diff --git a/lib/ui/home/home_ui.dart b/lib/ui/home/home_ui.dart index 8e49ca6..ed0c709 100644 --- a/lib/ui/home/home_ui.dart +++ b/lib/ui/home/home_ui.dart @@ -167,7 +167,7 @@ class HomeUI extends HookConsumerWidget { )), const SizedBox(width: 12), Button( - onPressed: () {}, + onPressed: () {}, child: const Padding( padding: EdgeInsets.all(6), child: Icon(FluentIcons.folder_open), @@ -788,15 +788,10 @@ class HomeUI extends HookConsumerWidget { _onMenuTap(BuildContext context, String key, HomeUIModelState homeState, WidgetRef ref) async { - String gameInstallReqInfo = - S.current.home_action_info_valid_install_location_required; + // String gameInstallReqInfo = + // S.current.home_action_info_valid_install_location_required; switch (key) { case "localization": - if (homeState.scInstalledPath == "not_install") { - // TODO - // ToolsUIModel.English(context, showNotGameInstallMsg: true); - break; - } final model = ref.watch(homeUIModelProvider.notifier); model.checkLocalizationUpdate(); await showDialog( @@ -807,14 +802,18 @@ class HomeUI extends HookConsumerWidget { model.checkLocalizationUpdate(skipReload: true); break; case "performance": - if (homeState.scInstalledPath == "not_install") { - showToast(context, gameInstallReqInfo); - break; - } - context.push("/index/$key"); - break; + return; + // if (homeState.scInstalledPath == "not_install") { + // showToast(context, gameInstallReqInfo); + // break; + // } + // context.push("/index/$key"); + // break; + case "game_doctor": + return; default: - context.push("/index/$key"); + return; + // context.push("/index/$key"); } } diff --git a/lib/ui/home/home_ui_model.g.dart b/lib/ui/home/home_ui_model.g.dart index f20a01d..87e23b1 100644 --- a/lib/ui/home/home_ui_model.g.dart +++ b/lib/ui/home/home_ui_model.g.dart @@ -6,7 +6,7 @@ part of 'home_ui_model.dart'; // RiverpodGenerator // ************************************************************************** -String _$homeUIModelHash() => r'6a768281606856766737a63aaeebb392c4613d2b'; +String _$homeUIModelHash() => r'422565027563e9bfd3ebddb08e518cade1c967d0'; /// See also [HomeUIModel]. @ProviderFor(HomeUIModel) diff --git a/lib/ui/home/localization/advanced_localization_ui_model.dart b/lib/ui/home/localization/advanced_localization_ui_model.dart index 5d20651..b86387b 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -9,7 +8,6 @@ import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; -import '../home_ui_model.dart'; import 'advanced_localization_ui.json.dart'; import 'localization_ui_model.dart'; @@ -178,40 +176,7 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { Future<(String, String)> _readIni(LocalizationUIState localizationUIState, LocalizationUIModel localizationUIModel) async { - final homeUIState = ref.read(homeUIModelProvider); - final gameDir = homeUIState.scInstalledPath; - if (gameDir == null) return ("", ""); - state = state.copyWith( - workingText: S.current.home_localization_advanced_msg_reading_p4k); - final p4kGlobalIni = await readEnglishInI(gameDir); - dPrint("read p4kGlobalIni => ${p4kGlobalIni.length}"); - state = state.copyWith( - workingText: S.current - .home_localization_advanced_msg_reading_server_localization_text); - - if (state.customizeGlobalIni != null) { - final apiLocalizationData = ScLocalizationData( - versionName: S.current.localization_info_custom_files, - info: "Customize"); - state = state.copyWith(apiLocalizationData: apiLocalizationData); - return (p4kGlobalIni, state.customizeGlobalIni!); - } else { - final apiLocalizationData = - localizationUIState.apiLocalizationData?.values.firstOrNull; - if (apiLocalizationData == null) return ("", ""); - final file = File( - "${localizationUIModel.getDownloadDir().absolute.path}\\${apiLocalizationData.versionName}.sclang"); - if (!await file.exists()) { - await localizationUIModel.downloadLocalizationFile( - file, apiLocalizationData); - } - state = state.copyWith(apiLocalizationData: apiLocalizationData); - final serverGlobalIni = - (await compute(LocalizationUIModel.readArchive, file.absolute.path)) - .toString(); - dPrint("read serverGlobalIni => ${serverGlobalIni.length}"); - return (p4kGlobalIni, serverGlobalIni); - } + return ("", ""); } Future readEnglishInI(String gameDir) async { 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 6a3fd17..6f9cd0e 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.g.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.g.dart @@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart'; // ************************************************************************** String _$advancedLocalizationUIModelHash() => - r'60ccd50f54b948d16be001f5ea07972a0fd9ed3f'; + r'5aa7e690f1db04febf5a747f4c8cd2eb79a0ed44'; /// See also [AdvancedLocalizationUIModel]. @ProviderFor(AdvancedLocalizationUIModel) diff --git a/lib/ui/home/localization/localization_dialog_ui.dart b/lib/ui/home/localization/localization_dialog_ui.dart index 5dd4d63..7f7b8b4 100644 --- a/lib/ui/home/localization/localization_dialog_ui.dart +++ b/lib/ui/home/localization/localization_dialog_ui.dart @@ -21,127 +21,19 @@ class LocalizationDialogUI extends HookConsumerWidget { useEffect(() { addPostFrameCallback(() { - model.checkUserCfg(context); + // model.checkUserCfg(context); }); return null; }, []); return ContentDialog( title: makeTitle(context, model, state), - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * .7, - minHeight: MediaQuery.of(context).size.height * .9), + constraints: const BoxConstraints(maxWidth: 1080, minHeight: 920), content: Padding( padding: const EdgeInsets.only(left: 12, right: 12, top: 12), child: SingleChildScrollView( child: Column( children: [ - AnimatedSize( - duration: const Duration(milliseconds: 130), - 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, - ), - ), - if (!(model.getScInstallPath() ?? "").contains("LIVE")) - Padding( - padding: const EdgeInsets.only(bottom: 12), - child: InfoBar( - title: Text(S.current - .home_localization_ptu_advanced_localization_tip_title), - content: Text(S.current - .home_localization_ptu_advanced_localization_tip_title_info), - severity: InfoBarSeverity.info, - style: InfoBarThemeData(decoration: (severity) { - return BoxDecoration(color: Colors.orange); - }, iconColor: (severity) { - return Colors.white; - }), - ), - ), - makeListContainer( - S.current.localization_info_translation_status, - [ - 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: [ - Text(S.current.localization_info_installed_version( - "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}")), - const Spacer(), - 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), - ], - ), - )), - ], - ), - ], - ), - ], - ], - context), makeListContainer( S.current.localization_info_community_translation, [ @@ -352,15 +244,37 @@ class LocalizationDialogUI extends HookConsumerWidget { const SizedBox(width: 12), Text(S.current.home_action_localization_management), const SizedBox(width: 24), - Text( - "${model.getScInstallPath()}", - style: const TextStyle(fontSize: 13), - ), + // Text( + // "${model.getScInstallPath()}", + // style: const TextStyle(fontSize: 13), + // ), const Spacer(), SizedBox( height: 36, child: Row( children: [ + Text( + S.current.localization_info_channel(""), + style: const TextStyle(fontSize: 16), + ), + const SizedBox(width: 12), + ComboBox( + value: state.selectedChannel, + items: [ + for (final channel in ["LIVE", "PTU"]) + ComboBoxItem( + value: channel, + child: Text(channel), + ) + ], + onChanged: state.workingVersion.isNotEmpty + ? null + : (v) { + if (v == null) return; + model.selectChannel(v); + }, + ), + const SizedBox(width: 24), Text( S.current.localization_info_language, style: const TextStyle(fontSize: 16), @@ -453,8 +367,7 @@ class LocalizationDialogUI extends HookConsumerWidget { ), confirm: S.current.localization_action_install, cancel: S.current.home_action_cancel, - constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .45), + constraints: const BoxConstraints(maxWidth: 720), ); if (userOK) { if (!context.mounted) return; diff --git a/lib/ui/home/localization/localization_ui_model.dart b/lib/ui/home/localization/localization_ui_model.dart index 24a59b9..22d7803 100644 --- a/lib/ui/home/localization/localization_ui_model.dart +++ b/lib/ui/home/localization/localization_ui_model.dart @@ -1,26 +1,24 @@ // ignore_for_file: avoid_build_context_in_providers import 'dart:async'; -import 'dart:io'; - -import 'package:archive/archive_io.dart'; +import 'package:archive/archive.dart'; +import 'dart:js_interop'; import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/api/api.dart'; -import 'package:starcitizen_doctor/common/conf/const_conf.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; -import 'package:starcitizen_doctor/common/utils/provider.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; import 'package:starcitizen_doctor/generated/no_l10n_strings.dart'; import 'package:starcitizen_doctor/ui/home/home_ui_model.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:web/web.dart' as web; + part 'localization_ui_model.g.dart'; part 'localization_ui_model.freezed.dart'; @@ -29,6 +27,7 @@ part 'localization_ui_model.freezed.dart'; class LocalizationUIState with _$LocalizationUIState { factory LocalizationUIState({ String? selectedLanguage, + @Default("LIVE") String selectedChannel, Map? apiLocalizationData, @Default("") String workingVersion, MapEntry? patchStatus, @@ -44,16 +43,6 @@ class LocalizationUIModel extends _$LocalizationUIModel { "chinese_(traditional)": NoL10n.langZHT, }; - Directory get _downloadDir => - Directory("${appGlobalState.applicationSupportDir}\\Localizations"); - - Directory getDownloadDir() => _downloadDir; - - Directory get _scDataDir => - Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data"); - - File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg"); - StreamSubscription? _customizeDirListenSub; String get _scInstallPath => ref.read(homeUIModelProvider).scInstalledPath!; @@ -66,9 +55,6 @@ class LocalizationUIModel extends _$LocalizationUIModel { } Future _init() async { - if (_scInstallPath == "not_install") { - return; - } ref.onDispose(() { _customizeDirListenSub?.cancel(); _customizeDirListenSub = null; @@ -85,14 +71,13 @@ class LocalizationUIModel extends _$LocalizationUIModel { Future _loadData() async { _allVersionLocalizationData.clear(); - await _updateStatus(); for (var lang in languageSupport.keys) { final l = await Api.getScLocalizationData(lang).unwrap(); if (l != null) { 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") { @@ -110,87 +95,19 @@ class LocalizationUIModel extends _$LocalizationUIModel { } } - void checkUserCfg(BuildContext context) async { - 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)); - if (ok == true) { - var finalString = ""; - for (var item in cfgString.split("\n")) { - if (!item.trim().startsWith("g_language")) { - finalString = "$finalString$item\n"; - } - } - await userCfgFile.delete(); - await userCfgFile.create(); - await userCfgFile.writeAsString(finalString, flush: true); - _loadData(); - } - } - } - } - - Future updateLangCfg(bool enable) async { + Future genLangCfg() async { final selectedLanguage = state.selectedLanguage!; - final status = await _getLangCfgEnableLang(lang: selectedLanguage); - final exists = await _cfgFile.exists(); - if (status == enable) { - await _updateStatus(); - return; - } StringBuffer newStr = StringBuffer(); - var str = []; - if (exists) { - str = (await _cfgFile.readAsString()).replaceAll(" ", "").split("\n"); + if (!newStr.toString().contains("sys_languages=$selectedLanguage")) { + newStr.writeln("sys_languages=$selectedLanguage"); } - if (enable) { - if (exists) { - for (var value in str) { - if (value.contains("sys_languages")) { - value = "sys_languages=$selectedLanguage"; - } else if (value.contains("g_language")) { - value = "g_language=$selectedLanguage"; - } else if (value.contains("g_languageAudio")) { - value = "g_language=english"; - } - if (value.trim().isNotEmpty) newStr.writeln(value); - } - } - 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"); - } - } else { - if (exists) { - for (var value in str) { - if (value.contains("sys_languages=")) { - continue; - } else if (value.contains("g_language")) { - continue; - } - newStr.writeln(value); - } - } + if (!newStr.toString().contains("g_language=$selectedLanguage")) { + newStr.writeln("g_language=$selectedLanguage"); } - if (exists) await _cfgFile.delete(recursive: true); - await _cfgFile.create(recursive: true); - await _cfgFile.writeAsString(newStr.toString()); - await _updateStatus(); + if (!newStr.toString().contains("g_languageAudio")) { + newStr.writeln("g_languageAudio=english"); + } + return newStr.toString(); } void goFeedback() { @@ -198,13 +115,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { } VoidCallback? doDelIniFile() { - return () async { - final iniFile = File( - "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); - if (await iniFile.exists()) await iniFile.delete(); - await updateLangCfg(false); - await _updateStatus(); - }; + return () async {}; } String getCustomizeFileName(String path) { @@ -214,8 +125,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { installFormString(StringBuffer globalIni, String versionName, {bool? advanced}) 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"); @@ -226,39 +136,40 @@ class LocalizationUIModel extends _$LocalizationUIModel { globalIni .write("_starcitizen_doctor_localization_version=$versionName\n"); } + final selectedLanguage = state.selectedLanguage!; + final iniFileString = "\uFEFF${globalIni.toString().trim()}"; + final cfg = await genLangCfg(); + final archive = Archive(); + archive.addFile(ArchiveFile( + "data/Localization/$selectedLanguage/global.ini", 0, iniFileString)); + archive.addFile(ArchiveFile("data/system.cfg", 0, 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); + jsDownloadBlobFile(url, "Localization_$versionName.zip"); + } - /// write cfg - if (await _cfgFile.exists()) {} - - /// write ini - if (await iniFile.exists()) { - await iniFile.delete(); - } - await iniFile.create(recursive: true); - await iniFile.writeAsString("\uFEFF${globalIni.toString().trim()}", - flush: true); - await updateLangCfg(true); - await _updateStatus(); + List? _encodeZipFile(Archive archive) { + final zip = ZipEncoder().encode(archive); + return zip; } VoidCallback? doRemoteInstall( BuildContext context, ScLocalizationData value) { return () async { - AnalyticsApi.touch("install_localization"); + // AnalyticsApi.touch("install_localization"); - final savePath = - File("${_downloadDir.absolute.path}\\${value.versionName}.sclang"); + // final savePath = + // File("${_downloadDir.absolute.path}\\${value.versionName}.sclang"); try { state = state.copyWith(workingVersion: value.versionName!); - if (!await savePath.exists()) { - // download - await downloadLocalizationFile(savePath, value); - } else { - dPrint("use cache $savePath"); - } + final data = await downloadLocalizationFile(value); await Future.delayed(const Duration(milliseconds: 300)); // check file - final globalIni = await compute(readArchive, savePath.absolute.path); + final globalIni = await compute(readArchive, data); if (globalIni.isEmpty) { throw S.current.localization_info_corrupted_file; } @@ -267,30 +178,25 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (!context.mounted) return; await showToast( context, S.current.localization_info_installation_error(e)); - if (await savePath.exists()) await savePath.delete(); } state = state.copyWith(workingVersion: ""); }; } - Future downloadLocalizationFile( - File savePath, ScLocalizationData value) async { - dPrint("downloading file to $savePath"); + Future downloadLocalizationFile(ScLocalizationData value) async { + dPrint("downloading downloadLocalizationFile ..."); final downloadUrl = "${URLConf.gitlabLocalizationUrl}/archive/${value.versionName}.tar.gz"; final r = await RSHttp.get(downloadUrl); if (r.statusCode == 200 && r.data != null) { - await savePath.create(recursive: true); - await savePath.writeAsBytes(r.data!, flush: true); + return r.data!; } else { throw "statusCode Error : ${r.statusCode}"; } } - static StringBuffer readArchive(String savePath) { - final inputStream = InputFileStream(savePath); - final archive = - TarDecoder().decodeBytes(GZipDecoder().decodeBuffer(inputStream)); + static StringBuffer readArchive(Uint8List data) { + final archive = TarDecoder().decodeBytes(GZipDecoder().decodeBytes(data)); StringBuffer globalIni = StringBuffer(""); for (var element in archive.files) { if (element.name.contains("global.ini")) { @@ -331,57 +237,6 @@ class LocalizationUIModel extends _$LocalizationUIModel { }; } - _updateStatus() async { - final patchStatus = MapEntry( - await _getLangCfgEnableLang(lang: state.selectedLanguage!), - await _getInstalledIniVersion( - "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini")); - final isInstalledAdvanced = await _checkAdvancedStatus( - "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); - state = state.copyWith( - patchStatus: patchStatus, isInstalledAdvanced: isInstalledAdvanced); - } - - Future _checkAdvancedStatus(String path) async { - final iniFile = File(path); - if (!await iniFile.exists()) { - return false; - } - final iniString = (await iniFile.readAsString()); - return iniString.contains("_starcitizen_doctor_localization_advanced=true"); - } - - Future _getLangCfgEnableLang( - {String lang = "", String gamePath = ""}) async { - if (gamePath.isEmpty) { - gamePath = _scInstallPath; - } - final cfgFile = File("${_scDataDir.absolute.path}\\system.cfg"); - if (!await cfgFile.exists()) return false; - final str = (await cfgFile.readAsString()).replaceAll(" ", ""); - return str.contains("sys_languages=$lang") && - str.contains("g_language=$lang") && - str.contains("g_languageAudio=english"); - } - - static Future _getInstalledIniVersion(String iniPath) async { - final iniFile = File(iniPath); - if (!await iniFile.exists()) { - return S.current.home_action_info_game_built_in; - } - final iniStringSplit = (await iniFile.readAsString()).split("\n"); - for (var i = iniStringSplit.length - 1; i > 0; i--) { - if (iniStringSplit[i] - .contains("_starcitizen_doctor_localization_version=")) { - final v = iniStringSplit[i] - .trim() - .split("_starcitizen_doctor_localization_version=")[1]; - return v; - } - } - return S.current.localization_info_custom_files; - } - Future> checkLangUpdate({bool skipReload = false}) async { if (_scInstallPath == "not_install") { return []; @@ -394,40 +249,29 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (homeState.scInstallPaths.isEmpty) return []; List updates = []; - - for (var scInstallPath in homeState.scInstallPaths) { - // 读取游戏安装文件夹 - final scDataDir = Directory("$scInstallPath\\data\\Localization"); - // 扫描目录确认已安装的语言 - final dirList = await scDataDir.list().toList(); - for (var element in dirList) { - for (var lang in languageSupport.keys) { - if (element.path.contains(lang) && - await _getLangCfgEnableLang( - lang: lang, gamePath: scInstallPath)) { - final installedVersion = - await _getInstalledIniVersion("${element.path}\\global.ini"); - if (installedVersion == S.current.home_action_info_game_built_in || - installedVersion == S.current.localization_info_custom_files) { - continue; - } - final curData = _allVersionLocalizationData[lang]; - dPrint("check Localization update $scInstallPath"); - if (!(curData?.keys.contains(installedVersion) ?? false)) { - // has update - for (var channel in ConstConf.gameChannels) { - if (scInstallPath.contains(channel)) { - dPrint("check Localization update: has update -> $channel"); - updates.add(channel); - } - } - } else { - dPrint("check Localization update: up to date"); - } - } - } - } - } return updates; } + + void selectChannel(String v) { + state = state.copyWith(selectedChannel: v); + _loadData(); + } } + +@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 aac3eb8..425872e 100644 --- a/lib/ui/home/localization/localization_ui_model.freezed.dart +++ b/lib/ui/home/localization/localization_ui_model.freezed.dart @@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$LocalizationUIState { String? get selectedLanguage => throw _privateConstructorUsedError; + String get selectedChannel => throw _privateConstructorUsedError; Map? get apiLocalizationData => throw _privateConstructorUsedError; String get workingVersion => throw _privateConstructorUsedError; @@ -39,6 +40,7 @@ abstract class $LocalizationUIStateCopyWith<$Res> { @useResult $Res call( {String? selectedLanguage, + String selectedChannel, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, @@ -62,6 +64,7 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> @override $Res call({ Object? selectedLanguage = freezed, + Object? selectedChannel = null, Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, @@ -73,6 +76,10 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> ? _value.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable as String?, + selectedChannel: null == selectedChannel + ? _value.selectedChannel + : selectedChannel // ignore: cast_nullable_to_non_nullable + as String, apiLocalizationData: freezed == apiLocalizationData ? _value.apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable @@ -107,6 +114,7 @@ abstract class _$$LocalizationUIStateImplCopyWith<$Res> @useResult $Res call( {String? selectedLanguage, + String selectedChannel, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, @@ -128,6 +136,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> @override $Res call({ Object? selectedLanguage = freezed, + Object? selectedChannel = null, Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, @@ -139,6 +148,10 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> ? _value.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable as String?, + selectedChannel: null == selectedChannel + ? _value.selectedChannel + : selectedChannel // ignore: cast_nullable_to_non_nullable + as String, apiLocalizationData: freezed == apiLocalizationData ? _value._apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable @@ -168,6 +181,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> class _$LocalizationUIStateImpl implements _LocalizationUIState { _$LocalizationUIStateImpl( {this.selectedLanguage, + this.selectedChannel = "LIVE", final Map? apiLocalizationData, this.workingVersion = "", this.patchStatus, @@ -178,6 +192,9 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { @override final String? selectedLanguage; + @override + @JsonKey() + final String selectedChannel; final Map? _apiLocalizationData; @override Map? get apiLocalizationData { @@ -208,7 +225,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { @override String toString() { - return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; + return 'LocalizationUIState(selectedLanguage: $selectedLanguage, selectedChannel: $selectedChannel, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; } @override @@ -218,6 +235,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { other is _$LocalizationUIStateImpl && (identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage) && + (identical(other.selectedChannel, selectedChannel) || + other.selectedChannel == selectedChannel) && const DeepCollectionEquality() .equals(other._apiLocalizationData, _apiLocalizationData) && (identical(other.workingVersion, workingVersion) || @@ -234,6 +253,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { int get hashCode => Object.hash( runtimeType, selectedLanguage, + selectedChannel, const DeepCollectionEquality().hash(_apiLocalizationData), workingVersion, patchStatus, @@ -253,6 +273,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { abstract class _LocalizationUIState implements LocalizationUIState { factory _LocalizationUIState( {final String? selectedLanguage, + final String selectedChannel, final Map? apiLocalizationData, final String workingVersion, final MapEntry? patchStatus, @@ -262,6 +283,8 @@ abstract class _LocalizationUIState implements LocalizationUIState { @override String? get selectedLanguage; @override + String get selectedChannel; + @override Map? get apiLocalizationData; @override String get workingVersion; diff --git a/lib/ui/home/localization/localization_ui_model.g.dart b/lib/ui/home/localization/localization_ui_model.g.dart index 86365ab..44d8b6e 100644 --- a/lib/ui/home/localization/localization_ui_model.g.dart +++ b/lib/ui/home/localization/localization_ui_model.g.dart @@ -7,7 +7,7 @@ part of 'localization_ui_model.dart'; // ************************************************************************** String _$localizationUIModelHash() => - r'd08a3d100c72b3f1f4a6c96944d4a73bb3c21808'; + r'404f40f1f2c04494ce570173262ee0d8579b30b4'; /// See also [LocalizationUIModel]. @ProviderFor(LocalizationUIModel) diff --git a/lib/ui/splash_ui.dart b/lib/ui/splash_ui.dart index d388573..39734a5 100644 --- a/lib/ui/splash_ui.dart +++ b/lib/ui/splash_ui.dart @@ -14,6 +14,8 @@ import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; +import 'package:web/web.dart' as web; + class SplashUI extends HookConsumerWidget { const SplashUI({super.key}); @@ -102,7 +104,7 @@ class SplashUI extends HookConsumerWidget { if (userOk) { await appConf.put("splash_alert_info_version", _alertInfoVersion); } else { - exit(0); + web.window.close(); } } } diff --git a/pubspec.yaml b/pubspec.yaml index a319cd7..46bba4a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: super_sliver_list: ^0.4.1 re_editor: ^0.3.0 re_highlight: ^0.0.3 + cross_file: ^0.3.4+2 + web: ^1.0.0 dependency_overrides: http: ^1.1.2 diff --git a/web/index.html b/web/index.html index 10b208f..a345276 100644 --- a/web/index.html +++ b/web/index.html @@ -1,40 +1,55 @@ - - + This is a placeholder for base href that will be replaced by the value of + the `--base-href` argument provided to `flutter build`. + --> + - - - + + + - - - - - + + + + + - - + + - starcitizentoolbox_web - - + starcitizentoolbox_web + + + - +