mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-07-13 12:16:08 +08:00
Init
This commit is contained in:
176
lib/app.dart
176
lib/app.dart
@ -1,37 +1,27 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.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';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.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/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 'data/app_version_data.dart';
|
||||
import 'generated/no_l10n_strings.dart';
|
||||
import 'ui/home/downloader/home_downloader_ui.dart';
|
||||
import 'ui/home/game_doctor/game_doctor_ui.dart';
|
||||
import 'ui/home/localization/advanced_localization_ui.dart';
|
||||
import 'ui/index_ui.dart';
|
||||
import 'ui/settings/upgrade_dialog.dart';
|
||||
import 'ui/tools/unp4kc/unp4kc_ui.dart';
|
||||
|
||||
part 'app.g.dart';
|
||||
|
||||
@ -47,32 +37,26 @@ 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(RouterRef ref) {
|
||||
return GoRouter(
|
||||
initialLocation: "/splash",
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
path: '/splash',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const SplashUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/index',
|
||||
path: '/',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const IndexUI()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "downloader",
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const HomeDownloaderUI())),
|
||||
GoRoute(
|
||||
path: 'game_doctor',
|
||||
pageBuilder: (context, state) =>
|
||||
myPageBuilder(context, state, const HomeGameDoctorUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'performance',
|
||||
pageBuilder: (context, state) =>
|
||||
@ -84,13 +68,7 @@ GoRouter router(RouterRef ref) {
|
||||
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: '/tools', builder: (_, __) => const SizedBox()),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -115,7 +93,7 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
Future<void> initApp() async {
|
||||
if (_initialized) return;
|
||||
// init Data
|
||||
final applicationSupportDir = await _initAppDir();
|
||||
// final applicationSupportDir = await _initAppDir();
|
||||
|
||||
// init Rust bridge
|
||||
await RustLib.init();
|
||||
@ -124,12 +102,12 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
|
||||
// init Hive
|
||||
try {
|
||||
Hive.init("$applicationSupportDir/db");
|
||||
// Hive.init("$applicationSupportDir/db");
|
||||
final box = await Hive.openBox("app_conf");
|
||||
state = state.copyWith(appConfBox: box);
|
||||
if (box.get("install_id", defaultValue: "") == "") {
|
||||
await box.put("install_id", const Uuid().v4());
|
||||
AnalyticsApi.touch("firstLaunch");
|
||||
// AnalyticsApi.touch("firstLaunch");
|
||||
}
|
||||
final deviceUUID = box.get("install_id", defaultValue: "");
|
||||
final localeCode = box.get("app_locale", defaultValue: null);
|
||||
@ -144,66 +122,50 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
}
|
||||
state = state.copyWith(deviceUUID: deviceUUID, appLocale: locale);
|
||||
} catch (e) {
|
||||
await win32.setForegroundWindow(windowName: "SCToolBox");
|
||||
// await win32.setForegroundWindow(windowName: "SCToolBox");
|
||||
dPrint("exit: db is locking ...");
|
||||
exit(0);
|
||||
// exit(0);
|
||||
}
|
||||
|
||||
// init powershell
|
||||
if (Platform.isWindows) {
|
||||
try {
|
||||
await SystemHelper.initPowershellPath();
|
||||
dPrint("---- Powershell init -----");
|
||||
} catch (e) {
|
||||
dPrint("powershell init failed : $e");
|
||||
}
|
||||
}
|
||||
|
||||
// get windows info
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dPrint("---- Window init -----");
|
||||
_startBackgroundLoop();
|
||||
_initialized = true;
|
||||
ref.keepAlive();
|
||||
}
|
||||
|
||||
Timer? _loopTimer;
|
||||
|
||||
_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 = Random();
|
||||
final index = random.nextInt(imageAssetsList.length);
|
||||
final image = imageAssetsList[index];
|
||||
state = state.copyWith(backgroundImageAssetsPath: image);
|
||||
dPrint("rollImage: [$index] $image");
|
||||
}
|
||||
|
||||
rollImage();
|
||||
// 使用 timer 每 30 秒 更换一次随机图片
|
||||
_loopTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
|
||||
rollImage();
|
||||
});
|
||||
}
|
||||
|
||||
String getUpgradePath() {
|
||||
return "${state.applicationSupportDir}/._upgrade";
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<bool> checkUpdate(BuildContext context) async {
|
||||
if (!ConstConf.isMSE) {
|
||||
final dir = Directory(getUpgradePath());
|
||||
if (await dir.exists()) {
|
||||
dir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
dynamic checkUpdateError;
|
||||
|
||||
try {
|
||||
final networkVersionData = await Api.getAppVersion();
|
||||
checkActivityThemeColor(networkVersionData);
|
||||
@ -229,26 +191,6 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
ConstConf.appVersionDate, checkUpdateError.toString()));
|
||||
return false;
|
||||
}
|
||||
if (!Platform.isWindows) return false;
|
||||
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());
|
||||
|
||||
if (r != true) {
|
||||
if (!context.mounted) return false;
|
||||
await showToast(context, S.current.app_common_upgrade_info_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -314,44 +256,6 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
state = state.copyWith(appLocale: value);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _initAppDir() async {
|
||||
if (Platform.isWindows) {
|
||||
final userProfileDir = Platform.environment["USERPROFILE"];
|
||||
final applicationSupportDir =
|
||||
(await getApplicationSupportDirectory()).absolute.path;
|
||||
String? applicationBinaryModuleDir;
|
||||
try {
|
||||
await initDPrintFile(applicationSupportDir);
|
||||
} catch (e) {
|
||||
dPrint("initDPrintFile Error: $e");
|
||||
}
|
||||
if (ConstConf.isMSE && userProfileDir != null) {
|
||||
applicationBinaryModuleDir =
|
||||
"$userProfileDir\\AppData\\Local\\Temp\\SCToolbox\\modules";
|
||||
} else {
|
||||
applicationBinaryModuleDir = "$applicationSupportDir\\modules";
|
||||
}
|
||||
dPrint("applicationSupportDir == $applicationSupportDir");
|
||||
dPrint("applicationBinaryModuleDir == $applicationBinaryModuleDir");
|
||||
state = state.copyWith(
|
||||
applicationSupportDir: applicationSupportDir,
|
||||
applicationBinaryModuleDir: applicationBinaryModuleDir,
|
||||
);
|
||||
return applicationSupportDir;
|
||||
} else {
|
||||
final applicationSupportDir =
|
||||
(await getApplicationSupportDirectory()).absolute.path;
|
||||
final applicationBinaryModuleDir = "$applicationSupportDir/modules";
|
||||
dPrint("applicationSupportDir == $applicationSupportDir");
|
||||
dPrint("applicationBinaryModuleDir == $applicationBinaryModuleDir");
|
||||
state = state.copyWith(
|
||||
applicationSupportDir: applicationSupportDir,
|
||||
applicationBinaryModuleDir: applicationBinaryModuleDir,
|
||||
);
|
||||
return applicationSupportDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -23,6 +23,7 @@ mixin _$AppGlobalState {
|
||||
ThemeConf get themeConf => throw _privateConstructorUsedError;
|
||||
Locale? get appLocale => throw _privateConstructorUsedError;
|
||||
Box<dynamic>? get appConfBox => throw _privateConstructorUsedError;
|
||||
String get backgroundImageAssetsPath => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -44,7 +45,8 @@ abstract class $AppGlobalStateCopyWith<$Res> {
|
||||
AppVersionData? networkVersionData,
|
||||
ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box<dynamic>? appConfBox});
|
||||
Box<dynamic>? appConfBox,
|
||||
String backgroundImageAssetsPath});
|
||||
|
||||
$ThemeConfCopyWith<$Res> get themeConf;
|
||||
}
|
||||
@ -71,6 +73,7 @@ class _$AppGlobalStateCopyWithImpl<$Res, $Val extends AppGlobalState>
|
||||
Object? themeConf = null,
|
||||
Object? appLocale = freezed,
|
||||
Object? appConfBox = freezed,
|
||||
Object? backgroundImageAssetsPath = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
deviceUUID: freezed == deviceUUID
|
||||
@ -101,6 +104,10 @@ class _$AppGlobalStateCopyWithImpl<$Res, $Val extends AppGlobalState>
|
||||
? _value.appConfBox
|
||||
: appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box<dynamic>?,
|
||||
backgroundImageAssetsPath: null == backgroundImageAssetsPath
|
||||
? _value.backgroundImageAssetsPath
|
||||
: backgroundImageAssetsPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@ -130,7 +137,8 @@ abstract class _$$AppGlobalStateImplCopyWith<$Res>
|
||||
AppVersionData? networkVersionData,
|
||||
ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box<dynamic>? appConfBox});
|
||||
Box<dynamic>? appConfBox,
|
||||
String backgroundImageAssetsPath});
|
||||
|
||||
@override
|
||||
$ThemeConfCopyWith<$Res> get themeConf;
|
||||
@ -156,6 +164,7 @@ class __$$AppGlobalStateImplCopyWithImpl<$Res>
|
||||
Object? themeConf = null,
|
||||
Object? appLocale = freezed,
|
||||
Object? appConfBox = freezed,
|
||||
Object? backgroundImageAssetsPath = null,
|
||||
}) {
|
||||
return _then(_$AppGlobalStateImpl(
|
||||
deviceUUID: freezed == deviceUUID
|
||||
@ -186,6 +195,10 @@ class __$$AppGlobalStateImplCopyWithImpl<$Res>
|
||||
? _value.appConfBox
|
||||
: appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box<dynamic>?,
|
||||
backgroundImageAssetsPath: null == backgroundImageAssetsPath
|
||||
? _value.backgroundImageAssetsPath
|
||||
: backgroundImageAssetsPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -200,7 +213,9 @@ class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
this.networkVersionData,
|
||||
this.themeConf = const ThemeConf(),
|
||||
this.appLocale,
|
||||
this.appConfBox});
|
||||
this.appConfBox,
|
||||
this.backgroundImageAssetsPath =
|
||||
"assets/backgrounds/SC_01_Wallpaper_3840x2160.jpg"});
|
||||
|
||||
@override
|
||||
final String? deviceUUID;
|
||||
@ -217,10 +232,13 @@ class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
final Locale? appLocale;
|
||||
@override
|
||||
final Box<dynamic>? appConfBox;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String 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)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -243,7 +261,10 @@ class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
(identical(other.appLocale, appLocale) ||
|
||||
other.appLocale == appLocale) &&
|
||||
(identical(other.appConfBox, appConfBox) ||
|
||||
other.appConfBox == appConfBox));
|
||||
other.appConfBox == appConfBox) &&
|
||||
(identical(other.backgroundImageAssetsPath,
|
||||
backgroundImageAssetsPath) ||
|
||||
other.backgroundImageAssetsPath == backgroundImageAssetsPath));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -255,7 +276,8 @@ class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
networkVersionData,
|
||||
themeConf,
|
||||
appLocale,
|
||||
appConfBox);
|
||||
appConfBox,
|
||||
backgroundImageAssetsPath);
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -275,7 +297,8 @@ abstract class _AppGlobalState implements AppGlobalState {
|
||||
final AppVersionData? networkVersionData,
|
||||
final ThemeConf themeConf,
|
||||
final Locale? appLocale,
|
||||
final Box<dynamic>? appConfBox}) = _$AppGlobalStateImpl;
|
||||
final Box<dynamic>? appConfBox,
|
||||
final String backgroundImageAssetsPath}) = _$AppGlobalStateImpl;
|
||||
|
||||
@override
|
||||
String? get deviceUUID;
|
||||
@ -291,6 +314,8 @@ abstract class _AppGlobalState implements AppGlobalState {
|
||||
Locale? get appLocale;
|
||||
@override
|
||||
Box<dynamic>? get appConfBox;
|
||||
@override
|
||||
String get backgroundImageAssetsPath;
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
@ -6,7 +6,7 @@ part of 'app.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$routerHash() => r'4fb9802d06347972b530f17ea7d66724a6ded997';
|
||||
String _$routerHash() => r'b35ab7db63955c2d8507e811a5e0d202f65c04be';
|
||||
|
||||
/// See also [router].
|
||||
@ProviderFor(router)
|
||||
@ -20,7 +20,7 @@ final routerProvider = AutoDisposeProvider<GoRouter>.internal(
|
||||
);
|
||||
|
||||
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
|
||||
String _$appGlobalModelHash() => r'cf3d526a61cbadea4252c6f8e096a527d5cef50f';
|
||||
String _$appGlobalModelHash() => r'1fb13f5be4249fdeb2d78906e1bfe94031b76afc';
|
||||
|
||||
/// See also [AppGlobalModel].
|
||||
@ProviderFor(AppGlobalModel)
|
||||
|
@ -1,47 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class BinaryModuleConf {
|
||||
static const _modules = {
|
||||
"aria2c": "0",
|
||||
"unp4kc": "1",
|
||||
};
|
||||
|
||||
static Future extractModule(List<String> modules, String workingDir) async {
|
||||
for (var m in _modules.entries) {
|
||||
if (!modules.contains(m.key)) continue;
|
||||
final name = m.key;
|
||||
final version = m.value;
|
||||
final dir = "$workingDir\\$name";
|
||||
final versionFile = File("$dir\\version");
|
||||
if (kReleaseMode &&
|
||||
await versionFile.exists() &&
|
||||
(await versionFile.readAsString()).trim() == version) {
|
||||
dPrint(
|
||||
"BinaryModuleConf.extractModule skip $name version == $version");
|
||||
continue;
|
||||
}
|
||||
// write model file
|
||||
final zipBuffer = await rootBundle.load("assets/binary/$name.zip");
|
||||
final decoder = ZipDecoder().decodeBytes(zipBuffer.buffer.asUint8List());
|
||||
for (var value in decoder.files) {
|
||||
final filename = value.name;
|
||||
if (value.isFile) {
|
||||
final data = value.content as List<int>;
|
||||
final file = File('$dir\\$filename');
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(data);
|
||||
} else {
|
||||
await Directory('$dir\\$filename').create(recursive: true);
|
||||
}
|
||||
}
|
||||
// write version file
|
||||
await versionFile.writeAsString(version);
|
||||
dPrint("BinaryModuleConf.extractModule $name $dir");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
class ConstConf {
|
||||
static const String appVersion = "2.12.0";
|
||||
static const int appVersionCode = 54;
|
||||
static const String appVersionDate = "2024-6-28";
|
||||
static const String appVersion = "Lite";
|
||||
static const int appVersionCode = 0;
|
||||
static const String appVersionDate = "2024-09-04";
|
||||
static const gameChannels = ["LIVE", "PTU", "EPTU", "TECH-PREVIEW"];
|
||||
static const isMSE =
|
||||
String.fromEnvironment("MSE", defaultValue: "false") == "true";
|
||||
static const isMSE = true;
|
||||
}
|
||||
|
@ -37,24 +37,7 @@ class URLConf {
|
||||
static String get devReleaseUrl => "$gitApiHome/SCToolBox/Release/releases";
|
||||
|
||||
static Future<bool> checkHost() async {
|
||||
// 使用 DNS 获取可用列表
|
||||
final gitApiList =
|
||||
_genFinalList(await RSHttp.dnsLookupTxt("git.dns.scbox.org"));
|
||||
dPrint("DNS gitApiList ==== $gitApiList");
|
||||
final fasterGit = await getFasterUrl(gitApiList);
|
||||
dPrint("gitApiList.Faster ==== $fasterGit");
|
||||
if (fasterGit != null) {
|
||||
gitApiHome = fasterGit;
|
||||
}
|
||||
final rssApiList =
|
||||
_genFinalList(await RSHttp.dnsLookupTxt("rss.dns.scbox.org"));
|
||||
final fasterRss = await getFasterUrl(rssApiList);
|
||||
dPrint("DNS rssApiList ==== $rssApiList");
|
||||
dPrint("rssApiList.Faster ==== $fasterRss");
|
||||
if (fasterRss != null) {
|
||||
rssApiHome = fasterRss;
|
||||
}
|
||||
isUrlCheckPass = fasterGit != null && fasterRss != null;
|
||||
isUrlCheckPass = true;
|
||||
return isUrlCheckPass;
|
||||
}
|
||||
|
||||
@ -87,14 +70,4 @@ class URLConf {
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> _genFinalList(List<String> sList) {
|
||||
List<String> list = [];
|
||||
for (var ll in sList) {
|
||||
final ssList = ll.split(",");
|
||||
for (var value in ssList) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
@ -1,209 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class SCLoggerHelper {
|
||||
static Future<String?> getLogFilePath() async {
|
||||
if (!Platform.isWindows) return null;
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["appdata"];
|
||||
if (appDataPath == null) {
|
||||
return null;
|
||||
}
|
||||
final rsiLauncherPath = "$appDataPath\\rsilauncher";
|
||||
dPrint("rsiLauncherPath:$rsiLauncherPath");
|
||||
final jsonLogPath = "$rsiLauncherPath\\logs\\log.log";
|
||||
return jsonLogPath;
|
||||
}
|
||||
|
||||
static Future<String?> getShaderCachePath() async {
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["LOCALAPPDATA"];
|
||||
if (appDataPath == null) {
|
||||
return null;
|
||||
}
|
||||
final scCachePath = "$appDataPath\\Star Citizen";
|
||||
dPrint("getShaderCachePath === $scCachePath");
|
||||
return scCachePath;
|
||||
}
|
||||
|
||||
static Future<List?> getLauncherLogList() async {
|
||||
if (!Platform.isWindows) return [];
|
||||
try {
|
||||
final jsonLogPath = await getLogFilePath();
|
||||
if (jsonLogPath == null) throw "no file path";
|
||||
var jsonString = utf8.decode(await File(jsonLogPath).readAsBytes());
|
||||
if (jsonString.endsWith("\n")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
if (jsonString.endsWith(" ")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
if (jsonString.endsWith(",")) {
|
||||
jsonString = jsonString.substring(0, jsonString.length - 3);
|
||||
}
|
||||
return json.decode("[$jsonString]");
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<String>> getGameInstallPath(List listData,
|
||||
{bool checkExists = true,
|
||||
List<String> withVersion = const ["LIVE"]}) async {
|
||||
List<String> scInstallPaths = [];
|
||||
|
||||
checkAndAddPath(String path, bool checkExists) async {
|
||||
if (path.isNotEmpty && !scInstallPaths.contains(path)) {
|
||||
if (!checkExists) {
|
||||
dPrint("find installPath == $path");
|
||||
scInstallPaths.add(path);
|
||||
} else if (await File("$path/Bin64/StarCitizen.exe").exists() &&
|
||||
await File("$path/Data.p4k").exists()) {
|
||||
dPrint("find installPath == $path");
|
||||
scInstallPaths.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final path = confBox.get("custom_game_path");
|
||||
if (path != null && path != "") {
|
||||
for (var v in withVersion) {
|
||||
await checkAndAddPath("$path\\$v", checkExists);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (var v in withVersion) {
|
||||
for (var i = listData.length - 1; i > 0; i--) {
|
||||
final m = listData[i];
|
||||
final info = m["[browser][info] "];
|
||||
if (info is String) {
|
||||
String installPath = "";
|
||||
if (info.contains("Installing Star Citizen $v")) {
|
||||
installPath = "${info.split(" at ")[1]}\\$v";
|
||||
}
|
||||
if (info.contains("Verifying Star Citizen $v")) {
|
||||
installPath = "${info.split(" at ")[1]}\\$v";
|
||||
}
|
||||
if (info.contains("Launching Star Citizen $v from")) {
|
||||
installPath = info
|
||||
.replaceAll("Launching Star Citizen $v from (", "")
|
||||
.replaceAll(")", "");
|
||||
}
|
||||
await checkAndAddPath(installPath, checkExists);
|
||||
}
|
||||
}
|
||||
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
// 动态检测更多位置
|
||||
for (var v in withVersion) {
|
||||
for (var fileName in List.from(scInstallPaths)) {
|
||||
if (fileName.toString().endsWith(v)) {
|
||||
for (var nv in withVersion) {
|
||||
final nextName =
|
||||
"${fileName.toString().replaceAll("\\$v", "")}\\$nv";
|
||||
await checkAndAddPath(nextName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
if (scInstallPaths.isEmpty) rethrow;
|
||||
}
|
||||
|
||||
return scInstallPaths;
|
||||
}
|
||||
|
||||
static String getGameChannelID(String installPath) {
|
||||
for (var value in ConstConf.gameChannels) {
|
||||
if (installPath.endsWith("\\$value")) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static Future<List<String>?> getGameRunningLogs(String gameDir) async {
|
||||
final logFile = File("$gameDir/Game.log");
|
||||
if (!await logFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
return await logFile.readAsLines(
|
||||
encoding: const Utf8Codec(allowMalformed: true));
|
||||
}
|
||||
|
||||
static MapEntry<String, String>? getGameRunningLogInfo(List<String> logs) {
|
||||
for (var i = logs.length - 1; i > 0; i--) {
|
||||
final line = logs[i];
|
||||
final r = _checkRunningLine(line);
|
||||
if (r != null) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static MapEntry<String, String>? _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);
|
||||
}
|
||||
if (line.contains("EXCEPTION_ACCESS_VIOLATION")) {
|
||||
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");
|
||||
}
|
||||
if (line.contains("Wakeup socket sendto error")) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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
|
||||
if (line.contains("network.replicatedEntityHandle")) {
|
||||
return const MapEntry("_", "network.replicatedEntityHandle");
|
||||
}
|
||||
if (line.contains("Exception Unknown")) {
|
||||
return const MapEntry("_", "Exception Unknown");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,280 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class SystemHelper {
|
||||
static String powershellPath = "powershell.exe";
|
||||
|
||||
static initPowershellPath() async {
|
||||
try {
|
||||
var result = await Process.run(powershellPath, ["echo", "ping"]);
|
||||
if (!result.stdout.toString().startsWith("ping") &&
|
||||
powershellPath == "powershell.exe") {
|
||||
throw "powershell check failed";
|
||||
}
|
||||
} catch (e) {
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final systemRoot = envVars["SYSTEMROOT"];
|
||||
if (systemRoot != null) {
|
||||
final autoSearchPath =
|
||||
"$systemRoot\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
|
||||
dPrint("auto search powershell path === $autoSearchPath");
|
||||
powershellPath = autoSearchPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> checkNvmePatchStatus() async {
|
||||
try {
|
||||
var result = await Process.run(SystemHelper.powershellPath, [
|
||||
"Get-ItemProperty",
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"\"ForcedPhysicalSectorSizeInBytes\""
|
||||
]);
|
||||
dPrint("checkNvmePatchStatus result ==== ${result.stdout}");
|
||||
if (result.stderr == "" &&
|
||||
result.stdout.toString().contains("{* 4095}")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> addNvmePatch() async {
|
||||
var result = await Process.run(powershellPath, [
|
||||
'New-ItemProperty',
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"ForcedPhysicalSectorSizeInBytes",
|
||||
"-PropertyType MultiString",
|
||||
"-Force -Value",
|
||||
"\"* 4095\""
|
||||
]);
|
||||
dPrint("nvme_PhysicalBytes result == ${result.stdout}");
|
||||
return result.stderr;
|
||||
}
|
||||
|
||||
static doRemoveNvmePath() async {
|
||||
try {
|
||||
var result = await Process.run(powershellPath, [
|
||||
"Clear-ItemProperty",
|
||||
"-Path",
|
||||
"\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"",
|
||||
"-Name",
|
||||
"\"ForcedPhysicalSectorSizeInBytes\""
|
||||
]);
|
||||
dPrint("doRemoveNvmePath result ==== ${result.stdout}");
|
||||
if (result.stderr == "") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取 RSI 启动器 目录
|
||||
static Future<String> getRSILauncherPath({bool skipEXE = false}) async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final path = confBox.get("custom_launcher_path");
|
||||
if (path != null && path != "") {
|
||||
if (await File(path).exists()) {
|
||||
if (skipEXE) {
|
||||
return "${path.toString().replaceAll("\\RSI Launcher.exe", "")}\\";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final programDataPath = envVars["programdata"];
|
||||
final rsiFilePath =
|
||||
"$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"
|
||||
]);
|
||||
if (r.stdout.toString().contains("RSI Launcher.exe")) {
|
||||
final start = r.stdout.toString().split("RSI Launcher.exe");
|
||||
if (skipEXE) {
|
||||
return start[0];
|
||||
}
|
||||
return "${start[0]}RSI Launcher.exe";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static killRSILauncher() async {
|
||||
var psr = await Process.run(
|
||||
powershellPath, ["ps", "\"RSI Launcher\"", "|select -expand id"]);
|
||||
if (psr.stderr == "") {
|
||||
for (var value in (psr.stdout ?? "").toString().split("\n")) {
|
||||
dPrint(value);
|
||||
if (value != "") {
|
||||
Process.killPid(int.parse(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<String>> getPID(String name) async {
|
||||
final r = await Process.run(powershellPath, ["(ps $name).Id"]);
|
||||
final str = r.stdout.toString().trim();
|
||||
dPrint(str);
|
||||
if (str.isEmpty) return [];
|
||||
return str.split("\n");
|
||||
}
|
||||
|
||||
static checkAndLaunchRSILauncher(String path) async {
|
||||
// check running and kill
|
||||
await killRSILauncher();
|
||||
// launch
|
||||
final processorAffinity = await SystemHelper.getCpuAffinity();
|
||||
if (processorAffinity == null) {
|
||||
Process.run(path, []);
|
||||
} else {
|
||||
Process.run("cmd.exe", [
|
||||
'/C',
|
||||
'Start',
|
||||
'""',
|
||||
'/High',
|
||||
'/Affinity',
|
||||
processorAffinity,
|
||||
path,
|
||||
]);
|
||||
}
|
||||
dPrint(path);
|
||||
}
|
||||
|
||||
static Future<int> getSystemMemorySizeGB() async {
|
||||
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;
|
||||
}
|
||||
|
||||
static Future<String> getSystemCimInstance(String win32InstanceName,
|
||||
{pathName = "Name"}) async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-CimInstance $win32InstanceName).$pathName"]);
|
||||
return r.stdout.toString().trim();
|
||||
}
|
||||
|
||||
static Future<String> getSystemName() async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-ComputerInfo | Select-Object -expand OsName)"]);
|
||||
return r.stdout.toString().trim();
|
||||
}
|
||||
|
||||
static Future<String> getCpuName() async {
|
||||
final r = await Process.run(
|
||||
powershellPath, ["(Get-WmiObject -Class Win32_Processor).Name"]);
|
||||
return r.stdout.toString().trim();
|
||||
}
|
||||
|
||||
static Future<String> getGpuInfo() async {
|
||||
const cmd = r"""
|
||||
$adapterMemory = (Get-ItemProperty -Path "HKLM:\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0*" -Name "HardwareInformation.AdapterString", "HardwareInformation.qwMemorySize" -Exclude PSPath -ErrorAction SilentlyContinue)
|
||||
foreach ($adapter in $adapterMemory) {
|
||||
[PSCustomObject] @{
|
||||
Model=$adapter."HardwareInformation.AdapterString"
|
||||
"VRAM (GB)"=[math]::round($adapter."HardwareInformation.qwMemorySize"/1GB)
|
||||
}
|
||||
}
|
||||
""";
|
||||
final r = await Process.run(powershellPath, [cmd]);
|
||||
return r.stdout.toString().trim();
|
||||
}
|
||||
|
||||
static Future<String> getDiskInfo() async {
|
||||
return (await Process.run(powershellPath,
|
||||
["Get-PhysicalDisk | format-table BusType,FriendlyName,Size"]))
|
||||
.stdout
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
|
||||
static Future<int> getDirLen(String path, {List<String>? skipPath}) async {
|
||||
if (path == "") return 0;
|
||||
int totalSize = 0;
|
||||
try {
|
||||
final l = await Directory(path).list(recursive: true).toList();
|
||||
for (var element in l) {
|
||||
if (element is File) {
|
||||
bool skip = false;
|
||||
if (skipPath != null) {
|
||||
for (var value in skipPath) {
|
||||
if (element.absolute.path.startsWith(value)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) totalSize += await element.length();
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
static Future<int> getNumberOfLogicalProcessors() async {
|
||||
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;
|
||||
}
|
||||
|
||||
static Future<String?> getCpuAffinity() async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final eCoreCount = int.tryParse(
|
||||
confBox.get("gameLaunch_eCore_count", defaultValue: "0")) ??
|
||||
0;
|
||||
final cpuNumber = await getNumberOfLogicalProcessors();
|
||||
if (cpuNumber == 0 || eCoreCount == 0 || eCoreCount > cpuNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuffer sb = StringBuffer();
|
||||
for (var i = 0; i < cpuNumber; i++) {
|
||||
if (i < eCoreCount) {
|
||||
sb.write("0");
|
||||
} else {
|
||||
sb.write("1");
|
||||
}
|
||||
}
|
||||
final binaryString = sb.toString();
|
||||
int hexDigits = (binaryString.length / 4).ceil();
|
||||
dPrint("Affinity sb ==== $sb");
|
||||
return int.parse(binaryString, radix: 2)
|
||||
.toRadixString(16)
|
||||
.padLeft(hexDigits, '0')
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
static Future openDir(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\"\""]);
|
||||
}
|
||||
}
|
||||
|
||||
static String getHostsFilePath() {
|
||||
if (Platform.isWindows) {
|
||||
final envVars = Platform.environment;
|
||||
final systemRoot = envVars["SYSTEMROOT"];
|
||||
return "$systemRoot\\System32\\drivers\\etc\\hosts";
|
||||
}
|
||||
return "/etc/hosts";
|
||||
}
|
||||
}
|
@ -17,10 +17,7 @@ class RSHttp {
|
||||
static Future<RustHttpResponse> get(String url,
|
||||
{Map<String, String>? headers, String? withIpAddress}) async {
|
||||
final r = await rust_http.fetch(
|
||||
method: MyMethod.gets,
|
||||
url: url,
|
||||
headers: headers,
|
||||
withIpAddress: withIpAddress);
|
||||
method: MyMethod.gets, url: url, headers: headers);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -42,29 +39,14 @@ class RSHttp {
|
||||
headers["Content-Type"] = contentType;
|
||||
}
|
||||
final r = await rust_http.fetch(
|
||||
method: MyMethod.post,
|
||||
url: url,
|
||||
headers: headers,
|
||||
inputData: data,
|
||||
withIpAddress: withIpAddress);
|
||||
method: MyMethod.post, url: url, headers: headers, inputData: data);
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<RustHttpResponse> head(String url,
|
||||
{Map<String, String>? headers, String? withIpAddress}) async {
|
||||
final r = await rust_http.fetch(
|
||||
method: MyMethod.head,
|
||||
url: url,
|
||||
headers: headers,
|
||||
withIpAddress: withIpAddress);
|
||||
method: MyMethod.head, url: url, headers: headers);
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<List<String>> dnsLookupTxt(String host) async {
|
||||
return await rust_http.dnsLookupTxt(host: host);
|
||||
}
|
||||
|
||||
static Future<List<String>> dnsLookupIps(String host) async {
|
||||
return await rust_http.dnsLookupIps(host: host);
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.3.0.
|
||||
|
||||
// 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<RsiLauncherAsarData> 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<void> writeMainJs({required List<int> 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;
|
||||
}
|
@ -16,20 +16,9 @@ Future<RustHttpResponse> fetch(
|
||||
{required MyMethod method,
|
||||
required String url,
|
||||
Map<String, String>? headers,
|
||||
Uint8List? inputData,
|
||||
String? withIpAddress}) =>
|
||||
Uint8List? inputData}) =>
|
||||
RustLib.instance.api.crateApiHttpApiFetch(
|
||||
method: method,
|
||||
url: url,
|
||||
headers: headers,
|
||||
inputData: inputData,
|
||||
withIpAddress: withIpAddress);
|
||||
|
||||
Future<List<String>> dnsLookupTxt({required String host}) =>
|
||||
RustLib.instance.api.crateApiHttpApiDnsLookupTxt(host: host);
|
||||
|
||||
Future<List<String>> dnsLookupIps({required String host}) =>
|
||||
RustLib.instance.api.crateApiHttpApiDnsLookupIps(host: host);
|
||||
method: method, url: url, headers: headers, inputData: inputData);
|
||||
|
||||
enum MyMethod {
|
||||
options,
|
||||
|
@ -1,54 +0,0 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.3.0.
|
||||
|
||||
// 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 not used by any `pub` functions: `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<RsProcessStreamData> start(
|
||||
{required String executable,
|
||||
required List<String> arguments,
|
||||
required String workingDirectory}) =>
|
||||
RustLib.instance.api.crateApiRsProcessStart(
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
workingDirectory: workingDirectory);
|
||||
|
||||
Future<void> 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,
|
||||
;
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.3.0.
|
||||
|
||||
// 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<void> sendNotify(
|
||||
{String? summary, String? body, String? appName, String? appId}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiSendNotify(
|
||||
summary: summary, body: body, appName: appName, appId: appId);
|
||||
|
||||
Future<bool> setForegroundWindow({required String windowName}) =>
|
||||
RustLib.instance.api
|
||||
.crateApiWin32ApiSetForegroundWindow(windowName: windowName);
|
@ -3,10 +3,7 @@
|
||||
|
||||
// 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';
|
||||
@ -70,7 +67,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.3.0';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1832496273;
|
||||
int get rustContentHash => -669496402;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@ -81,39 +78,14 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
}
|
||||
|
||||
abstract class RustLibApi extends BaseApi {
|
||||
Future<RsiLauncherAsarData> crateApiAsarApiGetRsiLauncherAsarData(
|
||||
{required String asarPath});
|
||||
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs(
|
||||
{required RsiLauncherAsarData that, required List<int> content});
|
||||
|
||||
Future<List<String>> crateApiHttpApiDnsLookupIps({required String host});
|
||||
|
||||
Future<List<String>> crateApiHttpApiDnsLookupTxt({required String host});
|
||||
|
||||
Future<RustHttpResponse> crateApiHttpApiFetch(
|
||||
{required MyMethod method,
|
||||
required String url,
|
||||
Map<String, String>? headers,
|
||||
Uint8List? inputData,
|
||||
String? withIpAddress});
|
||||
Uint8List? inputData});
|
||||
|
||||
Future<void> crateApiHttpApiSetDefaultHeader(
|
||||
{required Map<String, String> headers});
|
||||
|
||||
Stream<RsProcessStreamData> crateApiRsProcessStart(
|
||||
{required String executable,
|
||||
required List<String> arguments,
|
||||
required String workingDirectory});
|
||||
|
||||
Future<void> crateApiRsProcessWrite(
|
||||
{required int rsPid, required String data});
|
||||
|
||||
Future<void> crateApiWin32ApiSendNotify(
|
||||
{String? summary, String? body, String? appName, String? appId});
|
||||
|
||||
Future<bool> crateApiWin32ApiSetForegroundWindow(
|
||||
{required String windowName});
|
||||
}
|
||||
|
||||
class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
@ -124,134 +96,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<RsiLauncherAsarData> 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<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs(
|
||||
{required RsiLauncherAsarData that, required List<int> 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<List<String>> 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<List<String>> 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<RustHttpResponse> crateApiHttpApiFetch(
|
||||
{required MyMethod method,
|
||||
required String url,
|
||||
Map<String, String>? headers,
|
||||
Uint8List? inputData,
|
||||
String? withIpAddress}) {
|
||||
Uint8List? inputData}) {
|
||||
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(headers);
|
||||
var arg3 = cst_encode_opt_list_prim_u_8_strict(inputData);
|
||||
var arg4 = cst_encode_opt_String(withIpAddress);
|
||||
return wire.wire__crate__api__http_api__fetch(
|
||||
port_, arg0, arg1, arg2, arg3, arg4);
|
||||
port_, arg0, arg1, arg2, arg3);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_rust_http_response,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiHttpApiFetchConstMeta,
|
||||
argValues: [method, url, headers, inputData, withIpAddress],
|
||||
argValues: [method, url, headers, inputData],
|
||||
apiImpl: this,
|
||||
));
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiHttpApiFetchConstMeta => const TaskConstMeta(
|
||||
debugName: "fetch",
|
||||
argNames: ["method", "url", "headers", "inputData", "withIpAddress"],
|
||||
argNames: ["method", "url", "headers", "inputData"],
|
||||
);
|
||||
|
||||
@override
|
||||
@ -278,113 +150,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["headers"],
|
||||
);
|
||||
|
||||
@override
|
||||
Stream<RsProcessStreamData> crateApiRsProcessStart(
|
||||
{required String executable,
|
||||
required List<String> arguments,
|
||||
required String workingDirectory}) {
|
||||
final streamSink = RustStreamSink<RsProcessStreamData>();
|
||||
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<void> 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"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> 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<bool> 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"],
|
||||
);
|
||||
|
||||
@protected
|
||||
AnyhowException dco_decode_AnyhowException(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -398,32 +163,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
.map((e) => MapEntry(e.$1, e.$2)));
|
||||
}
|
||||
|
||||
@protected
|
||||
RustStreamSink<RsProcessStreamData>
|
||||
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
|
||||
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
|
||||
@ -436,18 +181,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw as int;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String> dco_decode_list_String(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_String).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as List<int>;
|
||||
}
|
||||
|
||||
@protected
|
||||
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -460,12 +193,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return (raw as List<dynamic>).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
|
||||
@ -478,12 +205,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_Map_String_String(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
|
||||
BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -509,52 +230,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
);
|
||||
}
|
||||
|
||||
@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<dynamic>;
|
||||
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<dynamic>;
|
||||
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<dynamic>;
|
||||
if (arr.length != 7)
|
||||
throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
|
||||
if (arr.length != 5)
|
||||
throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||
return RustHttpResponse(
|
||||
statusCode: dco_decode_u_16(arr[0]),
|
||||
headers: dco_decode_Map_String_String(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]),
|
||||
data: dco_decode_opt_list_prim_u_8_strict(arr[4]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -564,12 +251,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -603,14 +284,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return Map.fromEntries(inner.map((e) => MapEntry(e.$1, e.$2)));
|
||||
}
|
||||
|
||||
@protected
|
||||
RustStreamSink<RsProcessStreamData>
|
||||
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
|
||||
@ -618,19 +291,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
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
|
||||
@ -643,25 +303,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return deserializer.buffer.getInt32();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String> 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_ = <String>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_String(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> 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
|
||||
@ -682,13 +323,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -708,17 +342,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -750,38 +373,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -789,16 +380,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_headers = sse_decode_Map_String_String(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);
|
||||
}
|
||||
|
||||
@ -808,12 +395,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -832,9 +413,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
|
||||
@protected
|
||||
bool cst_encode_bool(bool raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
bool sse_decode_bool(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getUint8() != 0;
|
||||
}
|
||||
|
||||
@protected
|
||||
@ -843,36 +424,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -900,38 +463,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
self.entries.map((e) => (e.key, e.value)).toList(), serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_rs_process_stream_data_Dco(
|
||||
RustStreamSink<RsProcessStreamData> 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_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
|
||||
@ -944,24 +481,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
serializer.buffer.putInt32(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_String(List<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_String(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(
|
||||
List<int> 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) {
|
||||
@ -980,13 +499,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
@ -1004,16 +516,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@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_u_64(BigInt? self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -1043,31 +545,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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) {
|
||||
@ -1076,8 +553,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_Map_String_String(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);
|
||||
}
|
||||
|
||||
@ -1087,12 +562,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
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
|
||||
@ -1109,4 +578,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
void sse_encode_unit(void self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,7 @@
|
||||
|
||||
// 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;
|
||||
@ -28,50 +25,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Map<String, String> dco_decode_Map_String_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustStreamSink<RsProcessStreamData>
|
||||
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
|
||||
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<String> dco_decode_list_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<int> 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<String, String>? dco_decode_opt_Map_String_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
String? dco_decode_opt_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw);
|
||||
|
||||
@ -81,24 +55,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@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);
|
||||
|
||||
@ -115,33 +77,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
Map<String, String> sse_decode_Map_String_String(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
RustStreamSink<RsProcessStreamData>
|
||||
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
|
||||
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<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@ -149,9 +93,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
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);
|
||||
|
||||
@ -159,9 +100,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
Map<String, String>? sse_decode_opt_Map_String_String(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
String? sse_decode_opt_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer);
|
||||
|
||||
@ -172,27 +110,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
(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);
|
||||
|
||||
@ -202,6 +125,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
bool sse_decode_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_AnyhowException(
|
||||
AnyhowException raw) {
|
||||
@ -217,58 +143,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
raw.entries.map((e) => (e.key, e.value)).toList());
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>
|
||||
cst_encode_StreamSink_rs_process_stream_data_Dco(
|
||||
RustStreamSink<RsProcessStreamData> 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<wire_cst_list_prim_u_8_strict> 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<wire_cst_rsi_launcher_asar_data>
|
||||
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<ffi.Uint64> 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<wire_cst_list_String> cst_encode_list_String(List<String> 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<wire_cst_list_prim_u_8_loose> cst_encode_list_prim_u_8_loose(
|
||||
List<int> 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<wire_cst_list_prim_u_8_strict> cst_encode_list_prim_u_8_strict(
|
||||
Uint8List raw) {
|
||||
@ -296,13 +182,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return raw == null ? ffi.nullptr : cst_encode_Map_String_String(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<ffi.Uint64> cst_encode_opt_box_autoadd_u_64(BigInt? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
@ -322,13 +201,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return raw.toSigned(64).toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_box_autoadd_rsi_launcher_asar_data(
|
||||
RsiLauncherAsarData apiObj,
|
||||
ffi.Pointer<wire_cst_rsi_launcher_asar_data> 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) {
|
||||
@ -336,23 +208,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
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) {
|
||||
@ -361,32 +216,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
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);
|
||||
|
||||
@ -401,32 +242,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
void sse_encode_Map_String_String(
|
||||
Map<String, String> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_StreamSink_rs_process_stream_data_Dco(
|
||||
RustStreamSink<RsProcessStreamData> 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_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<String> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_strict(
|
||||
Uint8List self, SseSerializer serializer);
|
||||
@ -435,9 +259,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
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);
|
||||
|
||||
@ -445,9 +266,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
void sse_encode_opt_Map_String_String(
|
||||
Map<String, String>? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_String(String? self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer);
|
||||
|
||||
@ -459,18 +277,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
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);
|
||||
@ -478,9 +284,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@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);
|
||||
|
||||
@ -489,6 +292,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
@ -532,96 +338,12 @@ class RustLibWire implements BaseWire {
|
||||
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
|
||||
.asFunction<void Function(DartPostCObjectFnType)>();
|
||||
|
||||
void wire__crate__api__asar_api__get_rsi_launcher_asar_data(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_rsi_launcher_asar_data> that,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> 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<wire_cst_rsi_launcher_asar_data>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>)>>(
|
||||
'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<wire_cst_rsi_launcher_asar_data>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>)>();
|
||||
|
||||
void wire__crate__api__http_api__dns_lookup_ips(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__http_api__dns_lookup_txt(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__http_api__fetch(
|
||||
int port_,
|
||||
int method,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> url,
|
||||
ffi.Pointer<wire_cst_list_record_string_string> headers,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> input_data,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> with_ip_address,
|
||||
) {
|
||||
return _wire__crate__api__http_api__fetch(
|
||||
port_,
|
||||
@ -629,7 +351,6 @@ class RustLibWire implements BaseWire {
|
||||
url,
|
||||
headers,
|
||||
input_data,
|
||||
with_ip_address,
|
||||
);
|
||||
}
|
||||
|
||||
@ -640,7 +361,6 @@ class RustLibWire implements BaseWire {
|
||||
ffi.Int32,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_record_string_string>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__http_api__fetch');
|
||||
late final _wire__crate__api__http_api__fetch =
|
||||
@ -650,7 +370,6 @@ class RustLibWire implements BaseWire {
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_record_string_string>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__http_api__set_default_header(
|
||||
@ -673,128 +392,6 @@ class RustLibWire implements BaseWire {
|
||||
void Function(
|
||||
int, ffi.Pointer<wire_cst_list_record_string_string>)>();
|
||||
|
||||
void wire__crate__api__rs_process__start(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> executable,
|
||||
ffi.Pointer<wire_cst_list_String> arguments,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> working_directory,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__rs_process__write(
|
||||
int port_,
|
||||
int rs_pid,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__win32_api__send_notify(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> summary,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> body,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> app_name,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
void wire__crate__api__win32_api__set_foreground_window(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> 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<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'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<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
ffi.Pointer<wire_cst_rsi_launcher_asar_data>
|
||||
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<wire_cst_rsi_launcher_asar_data> 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<wire_cst_rsi_launcher_asar_data> Function()>();
|
||||
|
||||
ffi.Pointer<ffi.Uint64> cst_new_box_autoadd_u_64(
|
||||
int value,
|
||||
) {
|
||||
@ -809,36 +406,6 @@ class RustLibWire implements BaseWire {
|
||||
late final _cst_new_box_autoadd_u_64 = _cst_new_box_autoadd_u_64Ptr
|
||||
.asFunction<ffi.Pointer<ffi.Uint64> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_String> cst_new_list_String(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_String(
|
||||
len,
|
||||
);
|
||||
}
|
||||
|
||||
late final _cst_new_list_StringPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_String> Function(
|
||||
ffi.Int32)>>('frbgen_starcitizen_doctor_cst_new_list_String');
|
||||
late final _cst_new_list_String = _cst_new_list_StringPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_String> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> 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<wire_cst_list_prim_u_8_loose> 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<ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_new_list_prim_u_8_strict(
|
||||
int len,
|
||||
) {
|
||||
@ -899,21 +466,6 @@ final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_rsi_launcher_asar_data extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> asar_path;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> main_js_path;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> main_js_content;
|
||||
}
|
||||
|
||||
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_record_string_string extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field0;
|
||||
|
||||
@ -927,23 +479,6 @@ final class wire_cst_list_record_string_string extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_String extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Pointer<wire_cst_list_prim_u_8_strict>> 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<wire_cst_list_prim_u_8_strict> data;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int rs_pid;
|
||||
}
|
||||
|
||||
final class wire_cst_rust_http_response extends ffi.Struct {
|
||||
@ffi.Uint16()
|
||||
external int status_code;
|
||||
@ -954,10 +489,5 @@ final class wire_cst_rust_http_response extends ffi.Struct {
|
||||
|
||||
external ffi.Pointer<ffi.Uint64> content_length;
|
||||
|
||||
@ffi.Int32()
|
||||
external int version;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> remote_addr;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> data;
|
||||
}
|
||||
|
316
lib/common/rust/frb_generated.web.dart
Normal file
316
lib/common/rust/frb_generated.web.dart
Normal file
@ -0,0 +1,316 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// Generated by `flutter_rust_bridge`@ 2.3.0.
|
||||
|
||||
// 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
|
||||
|
||||
// Static analysis wrongly picks the IO variant, thus ignore this
|
||||
// ignore_for_file: argument_type_not_assignable
|
||||
|
||||
import 'api/http_api.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'frb_generated.dart';
|
||||
import 'http_package.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
|
||||
|
||||
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
RustLibApiImplPlatform({
|
||||
required super.handler,
|
||||
required super.wire,
|
||||
required super.generalizedFrbRustBinding,
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
@protected
|
||||
AnyhowException dco_decode_AnyhowException(dynamic raw);
|
||||
|
||||
@protected
|
||||
Map<String, String> dco_decode_Map_String_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
String dco_decode_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_box_autoadd_u_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_i_32(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
|
||||
MyMethod dco_decode_my_method(dynamic raw);
|
||||
|
||||
@protected
|
||||
Map<String, String>? dco_decode_opt_Map_String_String(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
|
||||
RustHttpResponse dco_decode_rust_http_response(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_16(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<String, String> sse_decode_Map_String_String(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
String sse_decode_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(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
|
||||
MyMethod sse_decode_my_method(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Map<String, String>? sse_decode_opt_Map_String_String(
|
||||
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
|
||||
RustHttpResponse sse_decode_rust_http_response(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_16(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
|
||||
bool sse_decode_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
String cst_encode_AnyhowException(AnyhowException raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_Map_String_String(Map<String, String> 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
|
||||
String cst_encode_String(String raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_box_autoadd_u_64(BigInt raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return cst_encode_u_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_list_prim_u_8_strict(Uint8List raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.jsify()!;
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_list_record_string_string(List<(String, String)> raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.map(cst_encode_record_string_string).toList().jsify()!;
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny? cst_encode_opt_Map_String_String(Map<String, String>? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw == null ? null : cst_encode_Map_String_String(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny? cst_encode_opt_box_autoadd_u_64(BigInt? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw == null ? null : cst_encode_box_autoadd_u_64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny? cst_encode_opt_list_prim_u_8_strict(Uint8List? raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw == null ? null : cst_encode_list_prim_u_8_strict(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_record_string_string((String, String) raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return [cst_encode_String(raw.$1), cst_encode_String(raw.$2)].jsify()!;
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_rust_http_response(RustHttpResponse raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return [
|
||||
cst_encode_u_16(raw.statusCode),
|
||||
cst_encode_Map_String_String(raw.headers),
|
||||
cst_encode_String(raw.url),
|
||||
cst_encode_opt_box_autoadd_u_64(raw.contentLength),
|
||||
cst_encode_opt_list_prim_u_8_strict(raw.data)
|
||||
].jsify()!;
|
||||
}
|
||||
|
||||
@protected
|
||||
JSAny cst_encode_u_64(BigInt raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return castNativeBigInt(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_i_32(int raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_my_method(MyMethod raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_u_16(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(
|
||||
Map<String, String> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_String(String 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_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_method(MyMethod self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_opt_Map_String_String(
|
||||
Map<String, String>? 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_rust_http_response(
|
||||
RustHttpResponse self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_16(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);
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
|
||||
class RustLibWire implements BaseWire {
|
||||
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
|
||||
|
||||
void wire__crate__api__http_api__fetch(NativePortType port_, int method,
|
||||
String url, JSAny? headers, JSAny? input_data) =>
|
||||
wasmModule.wire__crate__api__http_api__fetch(
|
||||
port_, method, url, headers, input_data);
|
||||
|
||||
void wire__crate__api__http_api__set_default_header(
|
||||
NativePortType port_, JSAny headers) =>
|
||||
wasmModule.wire__crate__api__http_api__set_default_header(port_, headers);
|
||||
}
|
||||
|
||||
@JS('wasm_bindgen')
|
||||
external RustLibWasmModule get wasmModule;
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
extension type RustLibWasmModule._(JSObject _) implements JSObject {
|
||||
external void wire__crate__api__http_api__fetch(NativePortType port_,
|
||||
int method, String url, JSAny? headers, JSAny? input_data);
|
||||
|
||||
external void wire__crate__api__http_api__set_default_header(
|
||||
NativePortType port_, JSAny headers);
|
||||
}
|
@ -6,23 +6,11 @@
|
||||
import 'frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
enum MyHttpVersion {
|
||||
http09,
|
||||
http10,
|
||||
http11,
|
||||
http2,
|
||||
http3,
|
||||
httpUnknown,
|
||||
;
|
||||
}
|
||||
|
||||
class RustHttpResponse {
|
||||
final int statusCode;
|
||||
final Map<String, String> headers;
|
||||
final String url;
|
||||
final BigInt? contentLength;
|
||||
final MyHttpVersion version;
|
||||
final String remoteAddr;
|
||||
final Uint8List? data;
|
||||
|
||||
const RustHttpResponse({
|
||||
@ -30,8 +18,6 @@ class RustHttpResponse {
|
||||
required this.headers,
|
||||
required this.url,
|
||||
this.contentLength,
|
||||
required this.version,
|
||||
required this.remoteAddr,
|
||||
this.data,
|
||||
});
|
||||
|
||||
@ -41,8 +27,6 @@ class RustHttpResponse {
|
||||
headers.hashCode ^
|
||||
url.hashCode ^
|
||||
contentLength.hashCode ^
|
||||
version.hashCode ^
|
||||
remoteAddr.hashCode ^
|
||||
data.hashCode;
|
||||
|
||||
@override
|
||||
@ -54,7 +38,5 @@ class RustHttpResponse {
|
||||
headers == other.headers &&
|
||||
url == other.url &&
|
||||
contentLength == other.contentLength &&
|
||||
version == other.version &&
|
||||
remoteAddr == other.remoteAddr &&
|
||||
data == other.data;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m1(v0, v1) =>
|
||||
"Network anomaly!\nThis may be that your network environment has DNS pollution, please try to replace DNS.\nOr the server is being maintained or attacked and tried it later.\nEnter the offline mode ...\n\nPlease use it carefully in the offline mode.\nThe current version of the construction date: ${v0}\n QQ group: 940696487\nError message: ${v1}";
|
||||
|
||||
static String m2(v0, v1) => "SCToolBox V ${v0} ${v1}";
|
||||
static String m2(v0, v1) => "SCToolBox ${v0} ${v1}";
|
||||
|
||||
static String m3(v0) => "Initialization failure: ${v0}";
|
||||
|
||||
@ -198,7 +198,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"about_app_description": MessageLookupByLibrary.simpleMessage(
|
||||
"Not just about localization!\n\nThe SCToolBox is your helper to explore the verse. We are committed to solving common in-game problems for citizens, in addition to facilitating localization from the community, game performance tuning, and localization of some commonly used website."),
|
||||
"about_check_update":
|
||||
MessageLookupByLibrary.simpleMessage("Check for updates"),
|
||||
MessageLookupByLibrary.simpleMessage("Get the full version"),
|
||||
"about_disclaimer": MessageLookupByLibrary.simpleMessage(
|
||||
"This is an unofficial Star Citizen fan-made tools, not affiliated with the Cloud Imperium group of companies. All content on this Software not authored by its host or users are property of their respective owners. \nStar Citizen®, Roberts Space Industries® and Cloud Imperium® are registered trademarks of Cloud Imperium Rights LLC."),
|
||||
"about_info_latest_version": MessageLookupByLibrary.simpleMessage(
|
||||
|
@ -20,7 +20,7 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ja';
|
||||
|
||||
static String m2(v0, v1) => "SCToolBox V ${v0} ${v1}";
|
||||
static String m2(v0, v1) => "SCToolBox ${v0} ${v1}";
|
||||
|
||||
static String m19(v0, v1) => "ダウンロード: ${v0}/s アップロード:${v1}/s";
|
||||
|
||||
|
@ -25,7 +25,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m1(v0, v1) =>
|
||||
"网络异常!\n这可能是您的网络环境存在DNS污染,请尝试更换DNS。\n或服务器正在维护或遭受攻击,稍后再试。 \n进入离线模式... \n\n请谨慎在离线模式中使用。 \n当前版本构建日期:${v0}\n QQ群:940696487 \n错误信息:${v1}";
|
||||
|
||||
static String m2(v0, v1) => "SC汉化盒子 V${v0} ${v1}";
|
||||
static String m2(v0, v1) => "SC汉化盒子 ${v0} ${v1}";
|
||||
|
||||
static String m3(v0) => "初始化失败:${v0}";
|
||||
|
||||
@ -180,7 +180,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"about_analytics_units_user": MessageLookupByLibrary.simpleMessage("位"),
|
||||
"about_app_description": MessageLookupByLibrary.simpleMessage(
|
||||
"不仅仅是汉化!\n\nSC汉化盒子是你探索宇宙的好帮手,我们致力于为各位公民解决游戏中的常见问题,并为社区汉化、性能调优、常用网站汉化 等操作提供便利。"),
|
||||
"about_check_update": MessageLookupByLibrary.simpleMessage("检查更新"),
|
||||
"about_check_update": MessageLookupByLibrary.simpleMessage("安装完整版"),
|
||||
"about_disclaimer": MessageLookupByLibrary.simpleMessage(
|
||||
"这是一个非官方的星际公民工具,不隶属于 Cloud Imperium 公司集团。 本软件中非由其主机或用户创作的所有内容均为其各自所有者的财产。 \nStar Citizen®、Roberts Space Industries® 和 Cloud Imperium® 是 Cloud Imperium Rights LLC 的注册商标。"),
|
||||
"about_info_latest_version":
|
||||
|
@ -25,7 +25,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m1(v0, v1) =>
|
||||
"網路異常!\n這可能是您的網路環境存在DNS汙染,請嘗試更換DNS。\n或伺服器正在維護或遭受攻擊,稍後再試。 \n進入離線模式... \n\n請謹慎在離線模式中使用。 \n目前版本構建日期:${v0}\n QQ群:940696487 \n錯誤資訊:${v1}";
|
||||
|
||||
static String m2(v0, v1) => "SC工具箱 V${v0} ${v1}";
|
||||
static String m2(v0, v1) => "SC工具箱 ${v0} ${v1}";
|
||||
|
||||
static String m3(v0) => "初始化失敗:${v0}";
|
||||
|
||||
@ -179,7 +179,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"about_analytics_units_user": MessageLookupByLibrary.simpleMessage("位"),
|
||||
"about_app_description": MessageLookupByLibrary.simpleMessage(
|
||||
"這個工具能做的不僅僅是翻譯!\n\nSC工具箱是你探索宇宙的好幫手,我們致力於為各位公民解決遊戲中的常見問題,並為社群翻譯、性能改善、常用網站翻譯 等操作提供便利。"),
|
||||
"about_check_update": MessageLookupByLibrary.simpleMessage("檢查更新"),
|
||||
"about_check_update": MessageLookupByLibrary.simpleMessage("安裝完整版"),
|
||||
"about_disclaimer": MessageLookupByLibrary.simpleMessage(
|
||||
"SC工具箱非官方的星際公民工具,不隸屬於 Cloud Imperium 公司集團。 本軟體中非由其主機或使用者創作的所有內容均為其各自所有者的財產。 \nStar Citizen®、Roberts Space Industries® 和 Cloud Imperium® 是 Cloud Imperium Rights LLC 的註冊商標。"),
|
||||
"about_info_latest_version":
|
||||
|
@ -70,10 +70,10 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `SCToolBox V {v0} {v1}`
|
||||
/// `SCToolBox {v0} {v1}`
|
||||
String app_index_version_info(Object v0, Object v1) {
|
||||
return Intl.message(
|
||||
'SCToolBox V $v0 $v1',
|
||||
'SCToolBox $v0 $v1',
|
||||
name: 'app_index_version_info',
|
||||
desc: '',
|
||||
args: [v0, v1],
|
||||
@ -90,10 +90,10 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Check for updates`
|
||||
/// `Get the full version`
|
||||
String get about_check_update {
|
||||
return Intl.message(
|
||||
'Check for updates',
|
||||
'Get the full version',
|
||||
name: 'about_check_update',
|
||||
desc: '',
|
||||
args: [],
|
||||
|
@ -5,11 +5,11 @@
|
||||
"@app_language_name": {},
|
||||
"app_language_code": "en",
|
||||
"@app_language_code": {},
|
||||
"app_index_version_info": "SCToolBox V {v0} {v1}",
|
||||
"app_index_version_info": "SCToolBox {v0} {v1}",
|
||||
"@app_index_version_info": {},
|
||||
"app_shortcut_name": "SCToolBox.lnk",
|
||||
"@app_shortcut_name": {},
|
||||
"about_check_update": "Check for updates",
|
||||
"about_check_update": "Get the full version",
|
||||
"@about_check_update": {},
|
||||
"about_app_description": "Not just about localization!\n\nThe SCToolBox is your helper to explore the verse. We are committed to solving common in-game problems for citizens, in addition to facilitating localization from the community, game performance tuning, and localization of some commonly used website.",
|
||||
"@about_app_description": {},
|
||||
|
@ -5,7 +5,7 @@
|
||||
"@app_language_name": {},
|
||||
"app_language_code": "ja",
|
||||
"@app_language_code": {},
|
||||
"app_index_version_info": "SCToolBox V {v0} {v1}",
|
||||
"app_index_version_info": "SCToolBox {v0} {v1}",
|
||||
"@app_index_version_info": {},
|
||||
"about_check_update": "更新チェック",
|
||||
"@about_check_update": {},
|
||||
|
@ -4,11 +4,11 @@
|
||||
"app_language_name": "简体中文",
|
||||
"@app_language_name": {},
|
||||
"app_language_code": "zh_CN",
|
||||
"app_index_version_info": "SC汉化盒子 V{v0} {v1}",
|
||||
"app_index_version_info": "SC汉化盒子 {v0} {v1}",
|
||||
"@app_index_version_info": {},
|
||||
"app_shortcut_name": "SC汉化盒子DEV.lnk",
|
||||
"@app_shortcut_name": {},
|
||||
"about_check_update": "检查更新",
|
||||
"about_check_update": "安装完整版",
|
||||
"@about_check_update": {},
|
||||
"about_app_description": "不仅仅是汉化!\n\nSC汉化盒子是你探索宇宙的好帮手,我们致力于为各位公民解决游戏中的常见问题,并为社区汉化、性能调优、常用网站汉化 等操作提供便利。",
|
||||
"@about_app_description": {},
|
||||
|
@ -4,11 +4,11 @@
|
||||
"app_language_name": "繁體中文",
|
||||
"@app_language_name": {},
|
||||
"app_language_code": "zh_TW",
|
||||
"app_index_version_info": "SC工具箱 V{v0} {v1}",
|
||||
"app_index_version_info": "SC工具箱 {v0} {v1}",
|
||||
"@app_index_version_info": {},
|
||||
"app_shortcut_name": "SC工具箱DEV.lnk",
|
||||
"@app_shortcut_name": {},
|
||||
"about_check_update": "檢查更新",
|
||||
"about_check_update": "安裝完整版",
|
||||
"@about_check_update": {},
|
||||
"about_app_description": "這個工具能做的不僅僅是翻譯!\n\nSC工具箱是你探索宇宙的好幫手,我們致力於為各位公民解決遊戲中的常見問題,並為社群翻譯、性能改善、常用網站翻譯 等操作提供便利。",
|
||||
"@about_app_description": {},
|
||||
|
@ -1,36 +1,16 @@
|
||||
import 'dart:io';
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'app.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
// webview window
|
||||
if (runWebViewTitleBarWidget(args,
|
||||
backgroundColor: const Color.fromRGBO(19, 36, 49, 1),
|
||||
builder: _defaultWebviewTitleBar)) {
|
||||
return;
|
||||
}
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await _initWindow();
|
||||
// run app
|
||||
runApp(const ProviderScope(child: App()));
|
||||
}
|
||||
|
||||
_initWindow() async {
|
||||
await windowManager.ensureInitialized();
|
||||
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);
|
||||
}
|
||||
|
||||
class App extends HookConsumerWidget {
|
||||
const App({super.key});
|
||||
@ -81,46 +61,3 @@ class App extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
],
|
||||
));
|
||||
}
|
||||
|
@ -1,216 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/binary_conf.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:aria2/aria2.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/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';
|
||||
|
||||
part 'aria2c.g.dart';
|
||||
|
||||
part 'aria2c.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class Aria2cModelState with _$Aria2cModelState {
|
||||
const factory Aria2cModelState({
|
||||
required String aria2cDir,
|
||||
Aria2c? aria2c,
|
||||
Aria2GlobalStat? aria2globalStat,
|
||||
}) = _Aria2cModelState;
|
||||
}
|
||||
|
||||
extension Aria2cModelExt on Aria2cModelState {
|
||||
bool get isRunning => aria2c != null;
|
||||
|
||||
bool get hasDownloadTask => aria2globalStat != null && aria2TotalTaskNum > 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
|
||||
() async {
|
||||
try {
|
||||
final sessionFile = File("$aria2cDir\\aria2.session");
|
||||
// 有下载任务则第一时间初始化
|
||||
if (await sessionFile.exists() &&
|
||||
(await sessionFile.readAsString()).trim().isNotEmpty) {
|
||||
dPrint("launch Aria2c daemon");
|
||||
await launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
} else {
|
||||
dPrint("LazyLoad Aria2c daemon");
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("Aria2cManager.checkLazyLoad Error:$e");
|
||||
}
|
||||
}();
|
||||
|
||||
return Aria2cModelState(aria2cDir: aria2cDir);
|
||||
}
|
||||
|
||||
Future launchDaemon(String applicationBinaryModuleDir) async {
|
||||
if (state.aria2c != null) return;
|
||||
await BinaryModuleConf.extractModule(
|
||||
["aria2c"], applicationBinaryModuleDir);
|
||||
|
||||
/// skip for debug hot reload
|
||||
if (kDebugMode) {
|
||||
if ((await SystemHelper.getPID("aria2c")).isNotEmpty) {
|
||||
dPrint("[Aria2cManager] debug skip for hot reload");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final sessionFile = File("${state.aria2cDir}\\aria2.session");
|
||||
if (!await sessionFile.exists()) {
|
||||
await sessionFile.create(recursive: true);
|
||||
}
|
||||
|
||||
final exePath = "${state.aria2cDir}\\aria2c.exe";
|
||||
final port = await getFreePort();
|
||||
final pwd = generateRandomPassword(16);
|
||||
dPrint("pwd === $pwd");
|
||||
final trackerList = await Api.getTorrentTrackerList();
|
||||
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);
|
||||
|
||||
String launchError = "";
|
||||
|
||||
stream.listen((event) {
|
||||
dPrint("Aria2cManager.rs_process event === $event");
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> getFreePort() async {
|
||||
final serverSocket = await ServerSocket.bind("127.0.0.1", 0);
|
||||
final port = serverSocket.port;
|
||||
await serverSocket.close();
|
||||
return port;
|
||||
}
|
||||
|
||||
String generateRandomPassword(int length) {
|
||||
const String charset =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = Random();
|
||||
StringBuffer buffer = StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int randomIndex = random.nextInt(charset.length);
|
||||
buffer.write(charset[randomIndex]);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
int textToByte(String text) {
|
||||
if (text.length == 1) {
|
||||
return 0;
|
||||
}
|
||||
if (int.tryParse(text) != null) {
|
||||
return int.parse(text);
|
||||
}
|
||||
if (text.endsWith("k")) {
|
||||
return int.parse(text.substring(0, text.length - 1)) * 1024;
|
||||
}
|
||||
if (text.endsWith("m")) {
|
||||
return int.parse(text.substring(0, text.length - 1)) * 1024 * 1024;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Future<void> _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<void> _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));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'aria2c.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Aria2cModelState {
|
||||
String get aria2cDir => throw _privateConstructorUsedError;
|
||||
Aria2c? get aria2c => throw _privateConstructorUsedError;
|
||||
Aria2GlobalStat? get aria2globalStat => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$Aria2cModelStateCopyWith<Aria2cModelState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $Aria2cModelStateCopyWith<$Res> {
|
||||
factory $Aria2cModelStateCopyWith(
|
||||
Aria2cModelState value, $Res Function(Aria2cModelState) then) =
|
||||
_$Aria2cModelStateCopyWithImpl<$Res, Aria2cModelState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$Aria2cModelStateCopyWithImpl<$Res, $Val extends Aria2cModelState>
|
||||
implements $Aria2cModelStateCopyWith<$Res> {
|
||||
_$Aria2cModelStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? aria2cDir = null,
|
||||
Object? aria2c = freezed,
|
||||
Object? aria2globalStat = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
aria2cDir: null == aria2cDir
|
||||
? _value.aria2cDir
|
||||
: aria2cDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
aria2c: freezed == aria2c
|
||||
? _value.aria2c
|
||||
: aria2c // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2c?,
|
||||
aria2globalStat: freezed == aria2globalStat
|
||||
? _value.aria2globalStat
|
||||
: aria2globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$Aria2cModelStateImplCopyWith<$Res>
|
||||
implements $Aria2cModelStateCopyWith<$Res> {
|
||||
factory _$$Aria2cModelStateImplCopyWith(_$Aria2cModelStateImpl value,
|
||||
$Res Function(_$Aria2cModelStateImpl) then) =
|
||||
__$$Aria2cModelStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$Aria2cModelStateImplCopyWithImpl<$Res>
|
||||
extends _$Aria2cModelStateCopyWithImpl<$Res, _$Aria2cModelStateImpl>
|
||||
implements _$$Aria2cModelStateImplCopyWith<$Res> {
|
||||
__$$Aria2cModelStateImplCopyWithImpl(_$Aria2cModelStateImpl _value,
|
||||
$Res Function(_$Aria2cModelStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? aria2cDir = null,
|
||||
Object? aria2c = freezed,
|
||||
Object? aria2globalStat = freezed,
|
||||
}) {
|
||||
return _then(_$Aria2cModelStateImpl(
|
||||
aria2cDir: null == aria2cDir
|
||||
? _value.aria2cDir
|
||||
: aria2cDir // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
aria2c: freezed == aria2c
|
||||
? _value.aria2c
|
||||
: aria2c // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2c?,
|
||||
aria2globalStat: freezed == aria2globalStat
|
||||
? _value.aria2globalStat
|
||||
: aria2globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$Aria2cModelStateImpl
|
||||
with DiagnosticableTreeMixin
|
||||
implements _Aria2cModelState {
|
||||
const _$Aria2cModelStateImpl(
|
||||
{required this.aria2cDir, this.aria2c, this.aria2globalStat});
|
||||
|
||||
@override
|
||||
final String aria2cDir;
|
||||
@override
|
||||
final Aria2c? aria2c;
|
||||
@override
|
||||
final Aria2GlobalStat? aria2globalStat;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'Aria2cModelState(aria2cDir: $aria2cDir, aria2c: $aria2c, aria2globalStat: $aria2globalStat)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Aria2cModelState'))
|
||||
..add(DiagnosticsProperty('aria2cDir', aria2cDir))
|
||||
..add(DiagnosticsProperty('aria2c', aria2c))
|
||||
..add(DiagnosticsProperty('aria2globalStat', aria2globalStat));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$Aria2cModelStateImpl &&
|
||||
(identical(other.aria2cDir, aria2cDir) ||
|
||||
other.aria2cDir == aria2cDir) &&
|
||||
(identical(other.aria2c, aria2c) || other.aria2c == aria2c) &&
|
||||
(identical(other.aria2globalStat, aria2globalStat) ||
|
||||
other.aria2globalStat == aria2globalStat));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, aria2cDir, aria2c, aria2globalStat);
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$Aria2cModelStateImplCopyWith<_$Aria2cModelStateImpl> get copyWith =>
|
||||
__$$Aria2cModelStateImplCopyWithImpl<_$Aria2cModelStateImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _Aria2cModelState implements Aria2cModelState {
|
||||
const factory _Aria2cModelState(
|
||||
{required final String aria2cDir,
|
||||
final Aria2c? aria2c,
|
||||
final Aria2GlobalStat? aria2globalStat}) = _$Aria2cModelStateImpl;
|
||||
|
||||
@override
|
||||
String get aria2cDir;
|
||||
@override
|
||||
Aria2c? get aria2c;
|
||||
@override
|
||||
Aria2GlobalStat? get aria2globalStat;
|
||||
|
||||
/// Create a copy of Aria2cModelState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$Aria2cModelStateImplCopyWith<_$Aria2cModelStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'aria2c.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$aria2cModelHash() => r'55dea5bd2e1c81fec0ef8ef1a10f41179775d7ee';
|
||||
|
||||
/// See also [Aria2cModel].
|
||||
@ProviderFor(Aria2cModel)
|
||||
final aria2cModelProvider =
|
||||
AutoDisposeNotifierProvider<Aria2cModel, Aria2cModelState>.internal(
|
||||
Aria2cModel.new,
|
||||
name: r'aria2cModelProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$aria2cModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$Aria2cModel = AutoDisposeNotifier<Aria2cModelState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,278 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
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';
|
||||
|
||||
part 'unp4kc.g.dart';
|
||||
|
||||
@freezed
|
||||
class Unp4kcState with _$Unp4kcState {
|
||||
const factory Unp4kcState({
|
||||
required bool startUp,
|
||||
Map<String, AppUnp4kP4kItemData>? files,
|
||||
MemoryFileSystem? fs,
|
||||
required String curPath,
|
||||
String? endMessage,
|
||||
MapEntry<String, String>? tempOpenFile,
|
||||
@Default("") String errorMessage,
|
||||
}) = _Unp4kcState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Unp4kCModel extends _$Unp4kCModel {
|
||||
int? _rsPid;
|
||||
|
||||
@override
|
||||
Unp4kcState build() {
|
||||
state = Unp4kcState(
|
||||
startUp: false,
|
||||
curPath: '\\',
|
||||
endMessage: S.current.tools_unp4k_msg_init);
|
||||
_init();
|
||||
return state;
|
||||
}
|
||||
|
||||
ToolsUIState get _toolsState => ref.read(toolsUIModelProvider);
|
||||
|
||||
String getGamePath() => _toolsState.scInstalledPath;
|
||||
|
||||
bool _hasUnp4kRunTimeError = false;
|
||||
|
||||
void _init() async {
|
||||
final execDir = "${appGlobalState.applicationBinaryModuleDir}\\unp4kc";
|
||||
await BinaryModuleConf.extractModule(
|
||||
["unp4kc"], appGlobalState.applicationBinaryModuleDir!);
|
||||
final exec = "$execDir\\unp4kc.exe";
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
ref.onDispose(() {
|
||||
state = state.copyWith(fs: null);
|
||||
if (_rsPid != null) {
|
||||
Process.killPid(_rsPid!);
|
||||
dPrint("[unp4kc] kill ...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DateTime? _loadStartTime;
|
||||
|
||||
void _handleMessage(Map<String, dynamic> eventJson, int rsPid) async {
|
||||
final action = eventJson["action"];
|
||||
final data = eventJson["data"];
|
||||
final gamePath = getGamePath();
|
||||
final gameP4kPath = "$gamePath\\Data.p4k";
|
||||
switch (action.toString().trim()) {
|
||||
case "info: startup":
|
||||
rs_process.write(rsPid: rsPid, data: "$gameP4kPath\n");
|
||||
break;
|
||||
case "info: Reading_p4k_file":
|
||||
_loadStartTime = DateTime.now();
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading);
|
||||
break;
|
||||
case "info: All Ready":
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading2);
|
||||
break;
|
||||
case "data: P4K_Files":
|
||||
final p4kFiles = (data as List<dynamic>);
|
||||
final files = <String, AppUnp4kP4kItemData>{};
|
||||
final fs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||
|
||||
var nextAwait = 0;
|
||||
for (var i = 0; i < p4kFiles.length; i++) {
|
||||
final item = AppUnp4kP4kItemData.fromJson(p4kFiles[i]);
|
||||
item.name = "${item.name}";
|
||||
files["\\${item.name}"] = item;
|
||||
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));
|
||||
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));
|
||||
_loadStartTime = null;
|
||||
break;
|
||||
case "info: Extracted_Open":
|
||||
final filePath = data.toString();
|
||||
dPrint("[unp4kc] Extracted_Open file: $filePath");
|
||||
const textExt = [".txt", ".xml", ".json", ".lua", ".cfg", ".ini"];
|
||||
const imgExt = [".png"];
|
||||
String openType = "unknown";
|
||||
for (var element in textExt) {
|
||||
if (filePath.toLowerCase().endsWith(element)) {
|
||||
openType = "text";
|
||||
}
|
||||
}
|
||||
for (var element in imgExt) {
|
||||
if (filePath.endsWith(element)) {
|
||||
openType = "image";
|
||||
}
|
||||
}
|
||||
state = state.copyWith(
|
||||
tempOpenFile: MapEntry(openType, filePath),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath));
|
||||
break;
|
||||
default:
|
||||
dPrint("[unp4kc] unknown action: $action");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<AppUnp4kP4kItemData>? getFiles() {
|
||||
final path = state.curPath.replaceAll("\\", "/");
|
||||
final fs = state.fs;
|
||||
if (fs == null) return null;
|
||||
final dir = fs.directory(path);
|
||||
if (!dir.existsSync()) return [];
|
||||
final files = dir.listSync(recursive: false, followLinks: false);
|
||||
files.sort((a, b) {
|
||||
if (a is Directory && b is File) {
|
||||
return -1;
|
||||
} else if (a is File && b is Directory) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.path.compareTo(b.path);
|
||||
}
|
||||
});
|
||||
final result = <AppUnp4kP4kItemData>[];
|
||||
for (var file in files) {
|
||||
if (file is File) {
|
||||
final f = state.files?[file.path.replaceAll("/", "\\")];
|
||||
if (f != null) {
|
||||
if (!(f.name?.startsWith("\\") ?? true)) {
|
||||
f.name = "\\${f.name}";
|
||||
}
|
||||
result.add(f);
|
||||
}
|
||||
} else {
|
||||
result.add(AppUnp4kP4kItemData(
|
||||
name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void changeDir(String name, {bool fullPath = false}) {
|
||||
if (fullPath) {
|
||||
state = state.copyWith(curPath: name);
|
||||
} else {
|
||||
state = state.copyWith(curPath: "${state.curPath}$name\\");
|
||||
}
|
||||
}
|
||||
|
||||
openFile(String filePath) async {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
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));
|
||||
extractFile(filePath, tempPath, mode: "extract_open");
|
||||
}
|
||||
|
||||
extractFile(String filePath, String outputPath,
|
||||
{String mode = "extract"}) async {
|
||||
// remove first \\
|
||||
if (filePath.startsWith("\\")) {
|
||||
filePath = filePath.substring(1);
|
||||
}
|
||||
outputPath = "$outputPath$filePath";
|
||||
dPrint("extractFile .... $filePath");
|
||||
if (_rsPid != null) {
|
||||
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")) {
|
||||
AnalyticsApi.touch("unp4k_no_runtime");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<Uint8List> unp4kTools(
|
||||
String applicationBinaryModuleDir, List<String> 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}");
|
||||
}
|
||||
final eventJson = await compute(json.decode, r.stdout.toString());
|
||||
if (eventJson["action"] == "data: Uint8List") {
|
||||
final data = eventJson["data"];
|
||||
return Uint8List.fromList((data as List).cast<int>());
|
||||
}
|
||||
throw Exception("error: data error");
|
||||
}
|
||||
}
|
@ -1,304 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'unp4kc.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Unp4kcState {
|
||||
bool get startUp => throw _privateConstructorUsedError;
|
||||
Map<String, AppUnp4kP4kItemData>? get files =>
|
||||
throw _privateConstructorUsedError;
|
||||
MemoryFileSystem? get fs => throw _privateConstructorUsedError;
|
||||
String get curPath => throw _privateConstructorUsedError;
|
||||
String? get endMessage => throw _privateConstructorUsedError;
|
||||
MapEntry<String, String>? get tempOpenFile =>
|
||||
throw _privateConstructorUsedError;
|
||||
String get errorMessage => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$Unp4kcStateCopyWith<Unp4kcState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $Unp4kcStateCopyWith<$Res> {
|
||||
factory $Unp4kcStateCopyWith(
|
||||
Unp4kcState value, $Res Function(Unp4kcState) then) =
|
||||
_$Unp4kcStateCopyWithImpl<$Res, Unp4kcState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool startUp,
|
||||
Map<String, AppUnp4kP4kItemData>? files,
|
||||
MemoryFileSystem? fs,
|
||||
String curPath,
|
||||
String? endMessage,
|
||||
MapEntry<String, String>? tempOpenFile,
|
||||
String errorMessage});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$Unp4kcStateCopyWithImpl<$Res, $Val extends Unp4kcState>
|
||||
implements $Unp4kcStateCopyWith<$Res> {
|
||||
_$Unp4kcStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? startUp = null,
|
||||
Object? files = freezed,
|
||||
Object? fs = freezed,
|
||||
Object? curPath = null,
|
||||
Object? endMessage = freezed,
|
||||
Object? tempOpenFile = freezed,
|
||||
Object? errorMessage = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
startUp: null == startUp
|
||||
? _value.startUp
|
||||
: startUp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
files: freezed == files
|
||||
? _value.files
|
||||
: files // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, AppUnp4kP4kItemData>?,
|
||||
fs: freezed == fs
|
||||
? _value.fs
|
||||
: fs // ignore: cast_nullable_to_non_nullable
|
||||
as MemoryFileSystem?,
|
||||
curPath: null == curPath
|
||||
? _value.curPath
|
||||
: curPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
endMessage: freezed == endMessage
|
||||
? _value.endMessage
|
||||
: endMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
tempOpenFile: freezed == tempOpenFile
|
||||
? _value.tempOpenFile
|
||||
: tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||
as MapEntry<String, String>?,
|
||||
errorMessage: null == errorMessage
|
||||
? _value.errorMessage
|
||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$Unp4kcStateImplCopyWith<$Res>
|
||||
implements $Unp4kcStateCopyWith<$Res> {
|
||||
factory _$$Unp4kcStateImplCopyWith(
|
||||
_$Unp4kcStateImpl value, $Res Function(_$Unp4kcStateImpl) then) =
|
||||
__$$Unp4kcStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool startUp,
|
||||
Map<String, AppUnp4kP4kItemData>? files,
|
||||
MemoryFileSystem? fs,
|
||||
String curPath,
|
||||
String? endMessage,
|
||||
MapEntry<String, String>? tempOpenFile,
|
||||
String errorMessage});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$Unp4kcStateImplCopyWithImpl<$Res>
|
||||
extends _$Unp4kcStateCopyWithImpl<$Res, _$Unp4kcStateImpl>
|
||||
implements _$$Unp4kcStateImplCopyWith<$Res> {
|
||||
__$$Unp4kcStateImplCopyWithImpl(
|
||||
_$Unp4kcStateImpl _value, $Res Function(_$Unp4kcStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? startUp = null,
|
||||
Object? files = freezed,
|
||||
Object? fs = freezed,
|
||||
Object? curPath = null,
|
||||
Object? endMessage = freezed,
|
||||
Object? tempOpenFile = freezed,
|
||||
Object? errorMessage = null,
|
||||
}) {
|
||||
return _then(_$Unp4kcStateImpl(
|
||||
startUp: null == startUp
|
||||
? _value.startUp
|
||||
: startUp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
files: freezed == files
|
||||
? _value._files
|
||||
: files // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, AppUnp4kP4kItemData>?,
|
||||
fs: freezed == fs
|
||||
? _value.fs
|
||||
: fs // ignore: cast_nullable_to_non_nullable
|
||||
as MemoryFileSystem?,
|
||||
curPath: null == curPath
|
||||
? _value.curPath
|
||||
: curPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
endMessage: freezed == endMessage
|
||||
? _value.endMessage
|
||||
: endMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
tempOpenFile: freezed == tempOpenFile
|
||||
? _value.tempOpenFile
|
||||
: tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||
as MapEntry<String, String>?,
|
||||
errorMessage: null == errorMessage
|
||||
? _value.errorMessage
|
||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$Unp4kcStateImpl with DiagnosticableTreeMixin implements _Unp4kcState {
|
||||
const _$Unp4kcStateImpl(
|
||||
{required this.startUp,
|
||||
final Map<String, AppUnp4kP4kItemData>? files,
|
||||
this.fs,
|
||||
required this.curPath,
|
||||
this.endMessage,
|
||||
this.tempOpenFile,
|
||||
this.errorMessage = ""})
|
||||
: _files = files;
|
||||
|
||||
@override
|
||||
final bool startUp;
|
||||
final Map<String, AppUnp4kP4kItemData>? _files;
|
||||
@override
|
||||
Map<String, AppUnp4kP4kItemData>? get files {
|
||||
final value = _files;
|
||||
if (value == null) return null;
|
||||
if (_files is EqualUnmodifiableMapView) return _files;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
final MemoryFileSystem? fs;
|
||||
@override
|
||||
final String curPath;
|
||||
@override
|
||||
final String? endMessage;
|
||||
@override
|
||||
final MapEntry<String, String>? tempOpenFile;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String errorMessage;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||
..add(DiagnosticsProperty('startUp', startUp))
|
||||
..add(DiagnosticsProperty('files', files))
|
||||
..add(DiagnosticsProperty('fs', fs))
|
||||
..add(DiagnosticsProperty('curPath', curPath))
|
||||
..add(DiagnosticsProperty('endMessage', endMessage))
|
||||
..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))
|
||||
..add(DiagnosticsProperty('errorMessage', errorMessage));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$Unp4kcStateImpl &&
|
||||
(identical(other.startUp, startUp) || other.startUp == startUp) &&
|
||||
const DeepCollectionEquality().equals(other._files, _files) &&
|
||||
(identical(other.fs, fs) || other.fs == fs) &&
|
||||
(identical(other.curPath, curPath) || other.curPath == curPath) &&
|
||||
(identical(other.endMessage, endMessage) ||
|
||||
other.endMessage == endMessage) &&
|
||||
(identical(other.tempOpenFile, tempOpenFile) ||
|
||||
other.tempOpenFile == tempOpenFile) &&
|
||||
(identical(other.errorMessage, errorMessage) ||
|
||||
other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
startUp,
|
||||
const DeepCollectionEquality().hash(_files),
|
||||
fs,
|
||||
curPath,
|
||||
endMessage,
|
||||
tempOpenFile,
|
||||
errorMessage);
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$Unp4kcStateImplCopyWith<_$Unp4kcStateImpl> get copyWith =>
|
||||
__$$Unp4kcStateImplCopyWithImpl<_$Unp4kcStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _Unp4kcState implements Unp4kcState {
|
||||
const factory _Unp4kcState(
|
||||
{required final bool startUp,
|
||||
final Map<String, AppUnp4kP4kItemData>? files,
|
||||
final MemoryFileSystem? fs,
|
||||
required final String curPath,
|
||||
final String? endMessage,
|
||||
final MapEntry<String, String>? tempOpenFile,
|
||||
final String errorMessage}) = _$Unp4kcStateImpl;
|
||||
|
||||
@override
|
||||
bool get startUp;
|
||||
@override
|
||||
Map<String, AppUnp4kP4kItemData>? get files;
|
||||
@override
|
||||
MemoryFileSystem? get fs;
|
||||
@override
|
||||
String get curPath;
|
||||
@override
|
||||
String? get endMessage;
|
||||
@override
|
||||
MapEntry<String, String>? get tempOpenFile;
|
||||
@override
|
||||
String get errorMessage;
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$Unp4kcStateImplCopyWith<_$Unp4kcStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'unp4kc.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$unp4kCModelHash() => r'636da3fe20d1fa94917dd63934f08f8dbffc8a9d';
|
||||
|
||||
/// See also [Unp4kCModel].
|
||||
@ProviderFor(Unp4kCModel)
|
||||
final unp4kCModelProvider =
|
||||
AutoDisposeNotifierProvider<Unp4kCModel, Unp4kcState>.internal(
|
||||
Unp4kCModel.new,
|
||||
name: r'unp4kCModelProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$unp4kCModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$Unp4kCModel = AutoDisposeNotifier<Unp4kcState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,121 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
import 'home_game_login_dialog_ui_model.dart';
|
||||
|
||||
class HomeGameLoginDialogUI extends HookConsumerWidget {
|
||||
final BuildContext launchContext;
|
||||
|
||||
const HomeGameLoginDialogUI(this.launchContext, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final loginState = ref.watch(homeGameLoginUIModelProvider);
|
||||
useEffect(() {
|
||||
ref
|
||||
.read(homeGameLoginUIModelProvider.notifier)
|
||||
.launchWebLogin(launchContext);
|
||||
return null;
|
||||
}, []);
|
||||
return ContentDialog(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .56,
|
||||
),
|
||||
title: (loginState.loginStatus == 2)
|
||||
? null
|
||||
: Text(S.current.home_action_one_click_launch),
|
||||
content: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 230),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Row(),
|
||||
if (loginState.loginStatus == 0) ...[
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(S.current.home_title_logging_in),
|
||||
const SizedBox(height: 12),
|
||||
const ProgressRing(),
|
||||
],
|
||||
),
|
||||
),
|
||||
] else if (loginState.loginStatus == 2 ||
|
||||
loginState.loginStatus == 3) ...[
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
S.current.home_login_title_welcome_back,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (loginState.avatarUrl != null)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
child: CacheNetImage(
|
||||
url: loginState.avatarUrl!,
|
||||
width: 128,
|
||||
height: 128,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
loginState.nickname ?? "",
|
||||
style: const TextStyle(
|
||||
fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (loginState.libraryData?.games != null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final game in loginState.libraryData!.games!)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12, right: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
FluentIcons.skype_circle_check,
|
||||
color: Colors.green,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text("${game.name}"),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24)
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.home_login_title_launching_game),
|
||||
const SizedBox(height: 12),
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/rsi_game_library_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/webview/webview.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
part 'home_game_login_dialog_ui_model.freezed.dart';
|
||||
|
||||
part 'home_game_login_dialog_ui_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class HomeGameLoginState with _$HomeGameLoginState {
|
||||
factory HomeGameLoginState({
|
||||
required int loginStatus,
|
||||
String? nickname,
|
||||
String? avatarUrl,
|
||||
String? authToken,
|
||||
String? webToken,
|
||||
Map? releaseInfo,
|
||||
RsiGameLibraryData? libraryData,
|
||||
String? installPath,
|
||||
bool? isDeviceSupportWinHello,
|
||||
}) = _LoginStatus;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class HomeGameLoginUIModel extends _$HomeGameLoginUIModel {
|
||||
@override
|
||||
HomeGameLoginState build() {
|
||||
return HomeGameLoginState(loginStatus: 0);
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<void> launchWebLogin(BuildContext context) async {
|
||||
final homeState = ref.read(homeUIModelProvider);
|
||||
if (!context.mounted) return;
|
||||
goWebView(context, S.current.home_action_login_rsi_account,
|
||||
"https://robertsspaceindustries.com/connect?jumpto=/connect",
|
||||
loginMode: true, rsiLoginCallback: (message, ok) async {
|
||||
// dPrint(
|
||||
// "======rsiLoginCallback=== $ok ===== data==\n${json.encode(message)}");
|
||||
if (message == null || !ok) {
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// final emailBox = await Hive.openBox("quick_login_email");
|
||||
final data = message["data"];
|
||||
final authToken = data["authToken"];
|
||||
final webToken = data["webToken"];
|
||||
final releaseInfo = data["releaseInfo"];
|
||||
final libraryData = RsiGameLibraryData.fromJson(data["libraryData"]);
|
||||
final avatarUrl = data["avatar"]
|
||||
?.toString()
|
||||
.replaceAll("url(\"", "")
|
||||
.replaceAll("\")", "");
|
||||
final Map<String, dynamic> payload = Jwt.parseJwt(authToken!);
|
||||
final nickname = payload["nickname"] ?? "";
|
||||
|
||||
state = state.copyWith(
|
||||
nickname: nickname,
|
||||
avatarUrl: avatarUrl,
|
||||
authToken: authToken,
|
||||
webToken: webToken,
|
||||
releaseInfo: releaseInfo,
|
||||
libraryData: libraryData,
|
||||
);
|
||||
|
||||
final buildInfoFile =
|
||||
File("${homeState.scInstalledPath}\\build_manifest.id");
|
||||
if (await buildInfoFile.exists()) {
|
||||
final buildInfo =
|
||||
json.decode(await buildInfoFile.readAsString())["Data"];
|
||||
|
||||
if (releaseInfo?["versionLabel"] != null &&
|
||||
buildInfo["RequestedP4ChangeNum"] != null) {
|
||||
if (!(releaseInfo!["versionLabel"]!
|
||||
.toString()
|
||||
.endsWith(buildInfo["RequestedP4ChangeNum"]!.toString()))) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.home_login_info_game_version_outdated,
|
||||
Text(S.current.home_login_info_rsi_server_report(
|
||||
releaseInfo?["versionLabel"],
|
||||
buildInfo["RequestedP4ChangeNum"])),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .4),
|
||||
cancel: S.current.home_login_info_action_ignore);
|
||||
if (ok == true) {
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
_readyForLaunch(homeState, context);
|
||||
}, useLocalization: true, homeState: homeState);
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
goWebView(BuildContext context, String title, String url,
|
||||
{bool useLocalization = false,
|
||||
bool loginMode = false,
|
||||
RsiLoginCallback? rsiLoginCallback,
|
||||
required HomeUIModelState homeState}) async {
|
||||
if (useLocalization) {
|
||||
const tipVersion = 2;
|
||||
final box = await Hive.openBox("app_conf");
|
||||
final skip = await box.get("skip_web_login_version", defaultValue: 0);
|
||||
if (skip != tipVersion) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.home_login_action_title_box_one_click_launch,
|
||||
Text(
|
||||
S.current.home_login_info_one_click_launch_description,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .6));
|
||||
if (!ok) {
|
||||
if (loginMode) {
|
||||
rsiLoginCallback?.call(null, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await box.put("skip_web_login_version", tipVersion);
|
||||
}
|
||||
}
|
||||
if (!await WebviewWindow.isWebviewAvailable()) {
|
||||
if (!context.mounted) return;
|
||||
await showToast(
|
||||
context, S.current.home_login_action_title_need_webview2_runtime);
|
||||
if (!context.mounted) return;
|
||||
await launchUrlString(
|
||||
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final webViewModel = WebViewModel(context,
|
||||
loginMode: loginMode,
|
||||
loginCallback: rsiLoginCallback,
|
||||
loginChannel: getChannelID(homeState.scInstalledPath!));
|
||||
if (useLocalization) {
|
||||
try {
|
||||
await webViewModel
|
||||
.initLocalization(homeState.webLocalizationVersionsData!);
|
||||
} catch (_) {}
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await webViewModel.initWebView(
|
||||
title: title,
|
||||
applicationSupportDir: appGlobalState.applicationSupportDir!,
|
||||
appVersionData: appGlobalState.networkVersionData!,
|
||||
);
|
||||
await webViewModel.launch(url, appGlobalState.networkVersionData!);
|
||||
}
|
||||
|
||||
Future<void> _readyForLaunch(
|
||||
HomeUIModelState homeState,
|
||||
// ignore: avoid_build_context_in_providers
|
||||
BuildContext context) async {
|
||||
final userBox = await Hive.openBox("rsi_account_data");
|
||||
state = state.copyWith(loginStatus: 2);
|
||||
final launchData = {
|
||||
"username": userBox.get("account_email", defaultValue: ""),
|
||||
"token": state.webToken,
|
||||
"auth_token": state.authToken,
|
||||
"star_network": {
|
||||
"services_endpoint": state.releaseInfo?["servicesEndpoint"],
|
||||
"hostname": state.releaseInfo?["universeHost"],
|
||||
"port": state.releaseInfo?["universePort"],
|
||||
},
|
||||
"TMid": const Uuid().v4(),
|
||||
};
|
||||
final executable = state.releaseInfo?["executable"];
|
||||
final launchOptions = state.releaseInfo?["launchOptions"];
|
||||
// dPrint("----------launch data ====== -----------\n$launchData");
|
||||
// dPrint(
|
||||
// "----------executable data ====== -----------\n${homeState.scInstalledPath}\\$executable $launchOptions");
|
||||
|
||||
final launchFile = File("${homeState.scInstalledPath}\\loginData.json");
|
||||
if (await launchFile.exists()) {
|
||||
await launchFile.delete();
|
||||
}
|
||||
await launchFile.create();
|
||||
await launchFile.writeAsString(json.encode(launchData));
|
||||
final processorAffinity = await SystemHelper.getCpuAffinity();
|
||||
final homeUIModel = ref.read(homeUIModelProvider.notifier);
|
||||
if (!context.mounted) return;
|
||||
homeUIModel.doLaunchGame(
|
||||
context,
|
||||
'${homeState.scInstalledPath}\\$executable',
|
||||
["-no_login_dialog", ...launchOptions.toString().split(" ")],
|
||||
homeState.scInstalledPath!,
|
||||
processorAffinity);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
String getChannelID(String installPath) {
|
||||
if (installPath.endsWith("\\LIVE")) {
|
||||
return "LIVE";
|
||||
}
|
||||
return "PTU";
|
||||
}
|
||||
}
|
@ -1,336 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'home_game_login_dialog_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$HomeGameLoginState {
|
||||
int get loginStatus => throw _privateConstructorUsedError;
|
||||
String? get nickname => throw _privateConstructorUsedError;
|
||||
String? get avatarUrl => throw _privateConstructorUsedError;
|
||||
String? get authToken => throw _privateConstructorUsedError;
|
||||
String? get webToken => throw _privateConstructorUsedError;
|
||||
Map<dynamic, dynamic>? get releaseInfo => throw _privateConstructorUsedError;
|
||||
RsiGameLibraryData? get libraryData => throw _privateConstructorUsedError;
|
||||
String? get installPath => throw _privateConstructorUsedError;
|
||||
bool? get isDeviceSupportWinHello => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of HomeGameLoginState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HomeGameLoginStateCopyWith<HomeGameLoginState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $HomeGameLoginStateCopyWith<$Res> {
|
||||
factory $HomeGameLoginStateCopyWith(
|
||||
HomeGameLoginState value, $Res Function(HomeGameLoginState) then) =
|
||||
_$HomeGameLoginStateCopyWithImpl<$Res, HomeGameLoginState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int loginStatus,
|
||||
String? nickname,
|
||||
String? avatarUrl,
|
||||
String? authToken,
|
||||
String? webToken,
|
||||
Map<dynamic, dynamic>? releaseInfo,
|
||||
RsiGameLibraryData? libraryData,
|
||||
String? installPath,
|
||||
bool? isDeviceSupportWinHello});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$HomeGameLoginStateCopyWithImpl<$Res, $Val extends HomeGameLoginState>
|
||||
implements $HomeGameLoginStateCopyWith<$Res> {
|
||||
_$HomeGameLoginStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of HomeGameLoginState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? loginStatus = null,
|
||||
Object? nickname = freezed,
|
||||
Object? avatarUrl = freezed,
|
||||
Object? authToken = freezed,
|
||||
Object? webToken = freezed,
|
||||
Object? releaseInfo = freezed,
|
||||
Object? libraryData = freezed,
|
||||
Object? installPath = freezed,
|
||||
Object? isDeviceSupportWinHello = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
loginStatus: null == loginStatus
|
||||
? _value.loginStatus
|
||||
: loginStatus // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
nickname: freezed == nickname
|
||||
? _value.nickname
|
||||
: nickname // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
avatarUrl: freezed == avatarUrl
|
||||
? _value.avatarUrl
|
||||
: avatarUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
authToken: freezed == authToken
|
||||
? _value.authToken
|
||||
: authToken // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
webToken: freezed == webToken
|
||||
? _value.webToken
|
||||
: webToken // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
releaseInfo: freezed == releaseInfo
|
||||
? _value.releaseInfo
|
||||
: releaseInfo // ignore: cast_nullable_to_non_nullable
|
||||
as Map<dynamic, dynamic>?,
|
||||
libraryData: freezed == libraryData
|
||||
? _value.libraryData
|
||||
: libraryData // ignore: cast_nullable_to_non_nullable
|
||||
as RsiGameLibraryData?,
|
||||
installPath: freezed == installPath
|
||||
? _value.installPath
|
||||
: installPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
isDeviceSupportWinHello: freezed == isDeviceSupportWinHello
|
||||
? _value.isDeviceSupportWinHello
|
||||
: isDeviceSupportWinHello // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$LoginStatusImplCopyWith<$Res>
|
||||
implements $HomeGameLoginStateCopyWith<$Res> {
|
||||
factory _$$LoginStatusImplCopyWith(
|
||||
_$LoginStatusImpl value, $Res Function(_$LoginStatusImpl) then) =
|
||||
__$$LoginStatusImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int loginStatus,
|
||||
String? nickname,
|
||||
String? avatarUrl,
|
||||
String? authToken,
|
||||
String? webToken,
|
||||
Map<dynamic, dynamic>? releaseInfo,
|
||||
RsiGameLibraryData? libraryData,
|
||||
String? installPath,
|
||||
bool? isDeviceSupportWinHello});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$LoginStatusImplCopyWithImpl<$Res>
|
||||
extends _$HomeGameLoginStateCopyWithImpl<$Res, _$LoginStatusImpl>
|
||||
implements _$$LoginStatusImplCopyWith<$Res> {
|
||||
__$$LoginStatusImplCopyWithImpl(
|
||||
_$LoginStatusImpl _value, $Res Function(_$LoginStatusImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of HomeGameLoginState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? loginStatus = null,
|
||||
Object? nickname = freezed,
|
||||
Object? avatarUrl = freezed,
|
||||
Object? authToken = freezed,
|
||||
Object? webToken = freezed,
|
||||
Object? releaseInfo = freezed,
|
||||
Object? libraryData = freezed,
|
||||
Object? installPath = freezed,
|
||||
Object? isDeviceSupportWinHello = freezed,
|
||||
}) {
|
||||
return _then(_$LoginStatusImpl(
|
||||
loginStatus: null == loginStatus
|
||||
? _value.loginStatus
|
||||
: loginStatus // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
nickname: freezed == nickname
|
||||
? _value.nickname
|
||||
: nickname // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
avatarUrl: freezed == avatarUrl
|
||||
? _value.avatarUrl
|
||||
: avatarUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
authToken: freezed == authToken
|
||||
? _value.authToken
|
||||
: authToken // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
webToken: freezed == webToken
|
||||
? _value.webToken
|
||||
: webToken // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
releaseInfo: freezed == releaseInfo
|
||||
? _value._releaseInfo
|
||||
: releaseInfo // ignore: cast_nullable_to_non_nullable
|
||||
as Map<dynamic, dynamic>?,
|
||||
libraryData: freezed == libraryData
|
||||
? _value.libraryData
|
||||
: libraryData // ignore: cast_nullable_to_non_nullable
|
||||
as RsiGameLibraryData?,
|
||||
installPath: freezed == installPath
|
||||
? _value.installPath
|
||||
: installPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
isDeviceSupportWinHello: freezed == isDeviceSupportWinHello
|
||||
? _value.isDeviceSupportWinHello
|
||||
: isDeviceSupportWinHello // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$LoginStatusImpl implements _LoginStatus {
|
||||
_$LoginStatusImpl(
|
||||
{required this.loginStatus,
|
||||
this.nickname,
|
||||
this.avatarUrl,
|
||||
this.authToken,
|
||||
this.webToken,
|
||||
final Map<dynamic, dynamic>? releaseInfo,
|
||||
this.libraryData,
|
||||
this.installPath,
|
||||
this.isDeviceSupportWinHello})
|
||||
: _releaseInfo = releaseInfo;
|
||||
|
||||
@override
|
||||
final int loginStatus;
|
||||
@override
|
||||
final String? nickname;
|
||||
@override
|
||||
final String? avatarUrl;
|
||||
@override
|
||||
final String? authToken;
|
||||
@override
|
||||
final String? webToken;
|
||||
final Map<dynamic, dynamic>? _releaseInfo;
|
||||
@override
|
||||
Map<dynamic, dynamic>? get releaseInfo {
|
||||
final value = _releaseInfo;
|
||||
if (value == null) return null;
|
||||
if (_releaseInfo is EqualUnmodifiableMapView) return _releaseInfo;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
final RsiGameLibraryData? libraryData;
|
||||
@override
|
||||
final String? installPath;
|
||||
@override
|
||||
final bool? isDeviceSupportWinHello;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HomeGameLoginState(loginStatus: $loginStatus, nickname: $nickname, avatarUrl: $avatarUrl, authToken: $authToken, webToken: $webToken, releaseInfo: $releaseInfo, libraryData: $libraryData, installPath: $installPath, isDeviceSupportWinHello: $isDeviceSupportWinHello)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$LoginStatusImpl &&
|
||||
(identical(other.loginStatus, loginStatus) ||
|
||||
other.loginStatus == loginStatus) &&
|
||||
(identical(other.nickname, nickname) ||
|
||||
other.nickname == nickname) &&
|
||||
(identical(other.avatarUrl, avatarUrl) ||
|
||||
other.avatarUrl == avatarUrl) &&
|
||||
(identical(other.authToken, authToken) ||
|
||||
other.authToken == authToken) &&
|
||||
(identical(other.webToken, webToken) ||
|
||||
other.webToken == webToken) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._releaseInfo, _releaseInfo) &&
|
||||
(identical(other.libraryData, libraryData) ||
|
||||
other.libraryData == libraryData) &&
|
||||
(identical(other.installPath, installPath) ||
|
||||
other.installPath == installPath) &&
|
||||
(identical(
|
||||
other.isDeviceSupportWinHello, isDeviceSupportWinHello) ||
|
||||
other.isDeviceSupportWinHello == isDeviceSupportWinHello));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
loginStatus,
|
||||
nickname,
|
||||
avatarUrl,
|
||||
authToken,
|
||||
webToken,
|
||||
const DeepCollectionEquality().hash(_releaseInfo),
|
||||
libraryData,
|
||||
installPath,
|
||||
isDeviceSupportWinHello);
|
||||
|
||||
/// Create a copy of HomeGameLoginState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoginStatusImplCopyWith<_$LoginStatusImpl> get copyWith =>
|
||||
__$$LoginStatusImplCopyWithImpl<_$LoginStatusImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _LoginStatus implements HomeGameLoginState {
|
||||
factory _LoginStatus(
|
||||
{required final int loginStatus,
|
||||
final String? nickname,
|
||||
final String? avatarUrl,
|
||||
final String? authToken,
|
||||
final String? webToken,
|
||||
final Map<dynamic, dynamic>? releaseInfo,
|
||||
final RsiGameLibraryData? libraryData,
|
||||
final String? installPath,
|
||||
final bool? isDeviceSupportWinHello}) = _$LoginStatusImpl;
|
||||
|
||||
@override
|
||||
int get loginStatus;
|
||||
@override
|
||||
String? get nickname;
|
||||
@override
|
||||
String? get avatarUrl;
|
||||
@override
|
||||
String? get authToken;
|
||||
@override
|
||||
String? get webToken;
|
||||
@override
|
||||
Map<dynamic, dynamic>? get releaseInfo;
|
||||
@override
|
||||
RsiGameLibraryData? get libraryData;
|
||||
@override
|
||||
String? get installPath;
|
||||
@override
|
||||
bool? get isDeviceSupportWinHello;
|
||||
|
||||
/// Create a copy of HomeGameLoginState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoginStatusImplCopyWith<_$LoginStatusImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'home_game_login_dialog_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$homeGameLoginUIModelHash() =>
|
||||
r'e8afccb7bba7c79e766e30a27f64128918a63dd7';
|
||||
|
||||
/// See also [HomeGameLoginUIModel].
|
||||
@ProviderFor(HomeGameLoginUIModel)
|
||||
final homeGameLoginUIModelProvider = AutoDisposeNotifierProvider<
|
||||
HomeGameLoginUIModel, HomeGameLoginState>.internal(
|
||||
HomeGameLoginUIModel.new,
|
||||
name: r'homeGameLoginUIModelProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$homeGameLoginUIModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$HomeGameLoginUIModel = AutoDisposeNotifier<HomeGameLoginState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,260 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:file_sizes/file_sizes.dart';
|
||||
|
||||
import 'home_downloader_ui_model.dart';
|
||||
|
||||
class HomeDownloaderUI extends HookConsumerWidget {
|
||||
const HomeDownloaderUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(homeDownloaderUIModelProvider);
|
||||
final model = ref.read(homeDownloaderUIModelProvider.notifier);
|
||||
|
||||
return makeDefaultPage(context,
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
for (final item in <MapEntry<String, IconData>, String>{
|
||||
const MapEntry("settings", FluentIcons.settings):
|
||||
S.current.downloader_speed_limit_settings,
|
||||
if (state.tasks.isNotEmpty)
|
||||
const MapEntry("pause_all", FluentIcons.pause):
|
||||
S.current.downloader_action_pause_all,
|
||||
if (state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("resume_all", FluentIcons.download):
|
||||
S.current.downloader_action_resume_all,
|
||||
if (state.tasks.isNotEmpty || state.waitingTasks.isNotEmpty)
|
||||
const MapEntry("cancel_all", FluentIcons.cancel):
|
||||
S.current.downloader_action_cancel_all,
|
||||
}.entries)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
child: Button(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(item.key.value),
|
||||
const SizedBox(width: 6),
|
||||
Text(item.value),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () =>
|
||||
model.onTapButton(context, item.key.key)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
if (model.getTasksLen() == 0)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(S.current.downloader_info_no_download_tasks),
|
||||
))
|
||||
else
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final (task, type, isFirstType) = model.getTaskAndType(index);
|
||||
final nt = HomeDownloaderUIModel.getTaskTypeAndName(task);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isFirstType)
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: index == 0 ? 0 : 12,
|
||||
bottom: 12),
|
||||
margin: const EdgeInsets.only(top: 6, bottom: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"${model.listHeaderStatusMap[type]}",
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12, right: 12, top: 12, bottom: 12),
|
||||
margin: const EdgeInsets.only(
|
||||
left: 12, right: 12, top: 6, bottom: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withOpacity(.06),
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
nt.value,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.downloader_info_total_size(
|
||||
FileSize.getSize(
|
||||
task.totalLength ?? 0)),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (nt.key == "torrent" &&
|
||||
task.verifiedLength != null &&
|
||||
task.verifiedLength != 0)
|
||||
Text(
|
||||
S.current.downloader_info_verifying(
|
||||
FileSize.getSize(
|
||||
task.verifiedLength)),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
)
|
||||
else if (task.status == "active")
|
||||
Text(S.current
|
||||
.downloader_info_downloading(
|
||||
((task.completedLength ?? 0) *
|
||||
100 /
|
||||
(task.totalLength ?? 1))
|
||||
.toStringAsFixed(4)))
|
||||
else
|
||||
Text(S.current.downloader_info_status(
|
||||
model.statusMap[task.status] ??
|
||||
"Unknown")),
|
||||
const SizedBox(width: 24),
|
||||
if (task.status == "active" &&
|
||||
task.verifiedLength == null)
|
||||
Text(
|
||||
"ETA: ${model.formatter.format(DateTime.now().add(Duration(seconds: model.getETA(task))))}"),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.downloader_info_uploaded(
|
||||
FileSize.getSize(task.uploadLength))),
|
||||
Text(S.current.downloader_info_downloaded(
|
||||
FileSize.getSize(task.completedLength))),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 18),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"↑:${FileSize.getSize(task.uploadSpeed)}/s"),
|
||||
Text(
|
||||
"↓:${FileSize.getSize(task.downloadSpeed)}/s"),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
if (type != "stopped")
|
||||
DropDownButton(
|
||||
closeAfterClick: false,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child:
|
||||
Text(S.current.downloader_action_options),
|
||||
),
|
||||
items: [
|
||||
if (task.status == "paused")
|
||||
MenuFlyoutItem(
|
||||
leading:
|
||||
const Icon(FluentIcons.download),
|
||||
text: Text(S.current
|
||||
.downloader_action_continue_download),
|
||||
onPressed: () =>
|
||||
model.resumeTask(task.gid))
|
||||
else if (task.status == "active")
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(FluentIcons.pause),
|
||||
text: Text(S.current
|
||||
.downloader_action_pause_download),
|
||||
onPressed: () =>
|
||||
model.pauseTask(task.gid)),
|
||||
const MenuFlyoutSeparator(),
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(
|
||||
FluentIcons.chrome_close,
|
||||
size: 14,
|
||||
),
|
||||
text: Text(S.current
|
||||
.downloader_action_cancel_download),
|
||||
onPressed: () =>
|
||||
model.cancelTask(context, task.gid)),
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(
|
||||
FluentIcons.folder_open,
|
||||
size: 14,
|
||||
),
|
||||
text: Text(S.current.action_open_folder),
|
||||
onPressed: () => model.openFolder(task)),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
itemCount: model.getTasksLen(),
|
||||
)),
|
||||
Container(
|
||||
color: FluentTheme.of(context).cardColor.withOpacity(.06),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, bottom: 3, top: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: state.isAvailable ? Colors.green : Colors.white,
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.downloader_info_download_upload_speed(
|
||||
FileSize.getSize(state.globalStat?.downloadSpeed ?? 0),
|
||||
FileSize.getSize(state.globalStat?.uploadSpeed ?? 0)))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
useBodyContainer: true);
|
||||
}
|
||||
}
|
@ -1,310 +0,0 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers, avoid_public_notifier_properties
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aria2/aria2.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
|
||||
import '../../../widgets/widgets.dart';
|
||||
|
||||
part 'home_downloader_ui_model.g.dart';
|
||||
|
||||
part 'home_downloader_ui_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class HomeDownloaderUIState with _$HomeDownloaderUIState {
|
||||
factory HomeDownloaderUIState({
|
||||
@Default([]) List<Aria2Task> tasks,
|
||||
@Default([]) List<Aria2Task> waitingTasks,
|
||||
@Default([]) List<Aria2Task> stoppedTasks,
|
||||
Aria2GlobalStat? globalStat,
|
||||
}) = _HomeDownloaderUIState;
|
||||
}
|
||||
|
||||
extension HomeDownloaderUIStateExtension on HomeDownloaderUIState {
|
||||
bool get isAvailable => globalStat != null;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
final DateFormat formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
final statusMap = {
|
||||
"active": S.current.downloader_info_downloading_status,
|
||||
"waiting": S.current.downloader_info_waiting,
|
||||
"paused": S.current.downloader_info_paused,
|
||||
"error": S.current.downloader_info_download_failed,
|
||||
"complete": S.current.downloader_info_download_completed,
|
||||
"removed": S.current.downloader_info_deleted,
|
||||
};
|
||||
|
||||
final listHeaderStatusMap = {
|
||||
"active": S.current.downloader_title_downloading,
|
||||
"waiting": S.current.downloader_info_waiting,
|
||||
"stopped": S.current.downloader_title_ended,
|
||||
};
|
||||
|
||||
@override
|
||||
HomeDownloaderUIState build() {
|
||||
state = HomeDownloaderUIState();
|
||||
_listenDownloader();
|
||||
ref.onDispose(() {
|
||||
_disposed = true;
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
onTapButton(BuildContext context, String key) async {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
switch (key) {
|
||||
case "pause_all":
|
||||
if (!aria2cState.isRunning) return;
|
||||
await aria2cState.aria2c?.pauseAll();
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
return;
|
||||
case "resume_all":
|
||||
if (!aria2cState.isRunning) return;
|
||||
await aria2cState.aria2c?.unpauseAll();
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
return;
|
||||
case "cancel_all":
|
||||
final userOK = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_all_tasks,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note));
|
||||
if (userOK == true) {
|
||||
if (!aria2cState.isRunning) return;
|
||||
try {
|
||||
for (var value in [...state.tasks, ...state.waitingTasks]) {
|
||||
await aria2cState.aria2c?.remove(value.gid!);
|
||||
}
|
||||
await aria2cState.aria2c?.saveSession();
|
||||
} catch (e) {
|
||||
dPrint("DownloadsUIModel cancel_all Error: $e");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case "settings":
|
||||
_showDownloadSpeedSettings(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int getTasksLen() {
|
||||
return state.tasks.length +
|
||||
state.waitingTasks.length +
|
||||
state.stoppedTasks.length;
|
||||
}
|
||||
|
||||
(Aria2Task, String, bool) getTaskAndType(int index) {
|
||||
final tempList = <Aria2Task>[
|
||||
...state.tasks,
|
||||
...state.waitingTasks,
|
||||
...state.stoppedTasks
|
||||
];
|
||||
if (index >= 0 && index < state.tasks.length) {
|
||||
return (tempList[index], "active", index == 0);
|
||||
}
|
||||
if (index >= state.tasks.length &&
|
||||
index < state.tasks.length + state.waitingTasks.length) {
|
||||
return (tempList[index], "waiting", index == state.tasks.length);
|
||||
}
|
||||
if (index >= state.tasks.length + state.waitingTasks.length &&
|
||||
index < tempList.length) {
|
||||
return (
|
||||
tempList[index],
|
||||
"stopped",
|
||||
index == state.tasks.length + state.waitingTasks.length
|
||||
);
|
||||
}
|
||||
throw Exception("Index out of range or element is null");
|
||||
}
|
||||
|
||||
static MapEntry<String, String> getTaskTypeAndName(Aria2Task task) {
|
||||
if (task.bittorrent == null) {
|
||||
String uri = task.files?[0]['uris'][0]['uri'] as String;
|
||||
return MapEntry("url", uri.split('/').last);
|
||||
} else if (task.bittorrent != null) {
|
||||
if (task.bittorrent!.containsKey('info')) {
|
||||
var btName = task.bittorrent?["info"]["name"];
|
||||
return MapEntry("torrent", btName ?? 'torrent');
|
||||
} else {
|
||||
return MapEntry("magnet", '[METADATA]${task.infoHash}');
|
||||
}
|
||||
} else {
|
||||
return const MapEntry("metaLink", '==========metaLink============');
|
||||
}
|
||||
}
|
||||
|
||||
int getETA(Aria2Task task) {
|
||||
if (task.downloadSpeed == null || task.downloadSpeed == 0) return 0;
|
||||
final remainingBytes =
|
||||
(task.totalLength ?? 0) - (task.completedLength ?? 0);
|
||||
return remainingBytes ~/ (task.downloadSpeed!);
|
||||
}
|
||||
|
||||
Future<void> resumeTask(String? gid) async {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
if (gid != null) {
|
||||
await aria2c?.unpause(gid);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> pauseTask(String? gid) async {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
if (gid != null) {
|
||||
await aria2c?.pause(gid);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cancelTask(BuildContext context, String? gid) async {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
if (gid != null) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_download,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note));
|
||||
if (ok == true) {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
await aria2c?.remove(gid);
|
||||
await aria2c?.saveSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Aria2File> getFilesFormTask(Aria2Task task) {
|
||||
List<Aria2File> l = [];
|
||||
if (task.files != null) {
|
||||
for (var element in task.files!) {
|
||||
final f = Aria2File.fromJson(element);
|
||||
l.add(f);
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
openFolder(Aria2Task task) {
|
||||
final f = getFilesFormTask(task).firstOrNull;
|
||||
if (f != null) {
|
||||
SystemHelper.openDir(File(f.path!).absolute.path.replaceAll("/", "\\"));
|
||||
}
|
||||
}
|
||||
|
||||
_listenDownloader() async {
|
||||
try {
|
||||
while (true) {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
if (_disposed) return;
|
||||
if (aria2cState.isRunning) {
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final tasks = await aria2c.tellActive();
|
||||
final waitingTasks = await aria2c.tellWaiting(0, 1000000);
|
||||
final stoppedTasks = await aria2c.tellStopped(0, 1000000);
|
||||
final globalStat = await aria2c.getGlobalStat();
|
||||
state = state.copyWith(
|
||||
tasks: tasks,
|
||||
waitingTasks: waitingTasks,
|
||||
stoppedTasks: stoppedTasks,
|
||||
globalStat: globalStat,
|
||||
);
|
||||
} else {
|
||||
state = state.copyWith(
|
||||
tasks: [],
|
||||
waitingTasks: [],
|
||||
stoppedTasks: [],
|
||||
globalStat: null,
|
||||
);
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("[DownloadsUIModel]._listenDownloader Error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showDownloadSpeedSettings(BuildContext context) async {
|
||||
final box = await Hive.openBox("app_conf");
|
||||
|
||||
final upCtrl = TextEditingController(
|
||||
text: box.get("downloader_up_limit", defaultValue: ""));
|
||||
final downCtrl = TextEditingController(
|
||||
text: box.get("downloader_down_limit", defaultValue: ""));
|
||||
|
||||
final ifr = FilteringTextInputFormatter.allow(RegExp(r'^\d*[km]?$'));
|
||||
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_speed_limit_settings,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
S.current.downloader_info_p2p_network_note,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white.withOpacity(.6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(S.current.downloader_info_download_unit_input_prompt),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_upload_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: upCtrl,
|
||||
placeholderStyle: TextStyle(color: Colors.white.withOpacity(.6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_download_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: downCtrl,
|
||||
placeholderStyle: TextStyle(color: Colors.white.withOpacity(.6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
S.current.downloader_input_info_p2p_upload_note,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withOpacity(.6),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
if (ok == true) {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
final aria2cModel = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cModel
|
||||
.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final upByte = aria2cModel.textToByte(upCtrl.text.trim());
|
||||
final downByte = aria2cModel.textToByte(downCtrl.text.trim());
|
||||
final r = await aria2c
|
||||
.changeGlobalOption(Aria2Option()
|
||||
..maxOverallUploadLimit = upByte
|
||||
..maxOverallDownloadLimit = downByte)
|
||||
.unwrap();
|
||||
if (r != null) {
|
||||
await box.put('downloader_up_limit', upCtrl.text.trim());
|
||||
await box.put('downloader_down_limit', downCtrl.text.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'home_downloader_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$HomeDownloaderUIState {
|
||||
List<Aria2Task> get tasks => throw _privateConstructorUsedError;
|
||||
List<Aria2Task> get waitingTasks => throw _privateConstructorUsedError;
|
||||
List<Aria2Task> get stoppedTasks => throw _privateConstructorUsedError;
|
||||
Aria2GlobalStat? get globalStat => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HomeDownloaderUIStateCopyWith<HomeDownloaderUIState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $HomeDownloaderUIStateCopyWith<$Res> {
|
||||
factory $HomeDownloaderUIStateCopyWith(HomeDownloaderUIState value,
|
||||
$Res Function(HomeDownloaderUIState) then) =
|
||||
_$HomeDownloaderUIStateCopyWithImpl<$Res, HomeDownloaderUIState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<Aria2Task> tasks,
|
||||
List<Aria2Task> waitingTasks,
|
||||
List<Aria2Task> stoppedTasks,
|
||||
Aria2GlobalStat? globalStat});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$HomeDownloaderUIStateCopyWithImpl<$Res,
|
||||
$Val extends HomeDownloaderUIState>
|
||||
implements $HomeDownloaderUIStateCopyWith<$Res> {
|
||||
_$HomeDownloaderUIStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? tasks = null,
|
||||
Object? waitingTasks = null,
|
||||
Object? stoppedTasks = null,
|
||||
Object? globalStat = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
tasks: null == tasks
|
||||
? _value.tasks
|
||||
: tasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
waitingTasks: null == waitingTasks
|
||||
? _value.waitingTasks
|
||||
: waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
stoppedTasks: null == stoppedTasks
|
||||
? _value.stoppedTasks
|
||||
: stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
globalStat: freezed == globalStat
|
||||
? _value.globalStat
|
||||
: globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$HomeDownloaderUIStateImplCopyWith<$Res>
|
||||
implements $HomeDownloaderUIStateCopyWith<$Res> {
|
||||
factory _$$HomeDownloaderUIStateImplCopyWith(
|
||||
_$HomeDownloaderUIStateImpl value,
|
||||
$Res Function(_$HomeDownloaderUIStateImpl) then) =
|
||||
__$$HomeDownloaderUIStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<Aria2Task> tasks,
|
||||
List<Aria2Task> waitingTasks,
|
||||
List<Aria2Task> stoppedTasks,
|
||||
Aria2GlobalStat? globalStat});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$HomeDownloaderUIStateImplCopyWithImpl<$Res>
|
||||
extends _$HomeDownloaderUIStateCopyWithImpl<$Res,
|
||||
_$HomeDownloaderUIStateImpl>
|
||||
implements _$$HomeDownloaderUIStateImplCopyWith<$Res> {
|
||||
__$$HomeDownloaderUIStateImplCopyWithImpl(_$HomeDownloaderUIStateImpl _value,
|
||||
$Res Function(_$HomeDownloaderUIStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? tasks = null,
|
||||
Object? waitingTasks = null,
|
||||
Object? stoppedTasks = null,
|
||||
Object? globalStat = freezed,
|
||||
}) {
|
||||
return _then(_$HomeDownloaderUIStateImpl(
|
||||
tasks: null == tasks
|
||||
? _value._tasks
|
||||
: tasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
waitingTasks: null == waitingTasks
|
||||
? _value._waitingTasks
|
||||
: waitingTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
stoppedTasks: null == stoppedTasks
|
||||
? _value._stoppedTasks
|
||||
: stoppedTasks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Aria2Task>,
|
||||
globalStat: freezed == globalStat
|
||||
? _value.globalStat
|
||||
: globalStat // ignore: cast_nullable_to_non_nullable
|
||||
as Aria2GlobalStat?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$HomeDownloaderUIStateImpl implements _HomeDownloaderUIState {
|
||||
_$HomeDownloaderUIStateImpl(
|
||||
{final List<Aria2Task> tasks = const [],
|
||||
final List<Aria2Task> waitingTasks = const [],
|
||||
final List<Aria2Task> stoppedTasks = const [],
|
||||
this.globalStat})
|
||||
: _tasks = tasks,
|
||||
_waitingTasks = waitingTasks,
|
||||
_stoppedTasks = stoppedTasks;
|
||||
|
||||
final List<Aria2Task> _tasks;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Aria2Task> get tasks {
|
||||
if (_tasks is EqualUnmodifiableListView) return _tasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_tasks);
|
||||
}
|
||||
|
||||
final List<Aria2Task> _waitingTasks;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Aria2Task> get waitingTasks {
|
||||
if (_waitingTasks is EqualUnmodifiableListView) return _waitingTasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_waitingTasks);
|
||||
}
|
||||
|
||||
final List<Aria2Task> _stoppedTasks;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Aria2Task> get stoppedTasks {
|
||||
if (_stoppedTasks is EqualUnmodifiableListView) return _stoppedTasks;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_stoppedTasks);
|
||||
}
|
||||
|
||||
@override
|
||||
final Aria2GlobalStat? globalStat;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HomeDownloaderUIState(tasks: $tasks, waitingTasks: $waitingTasks, stoppedTasks: $stoppedTasks, globalStat: $globalStat)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$HomeDownloaderUIStateImpl &&
|
||||
const DeepCollectionEquality().equals(other._tasks, _tasks) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._waitingTasks, _waitingTasks) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._stoppedTasks, _stoppedTasks) &&
|
||||
(identical(other.globalStat, globalStat) ||
|
||||
other.globalStat == globalStat));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_tasks),
|
||||
const DeepCollectionEquality().hash(_waitingTasks),
|
||||
const DeepCollectionEquality().hash(_stoppedTasks),
|
||||
globalStat);
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$HomeDownloaderUIStateImplCopyWith<_$HomeDownloaderUIStateImpl>
|
||||
get copyWith => __$$HomeDownloaderUIStateImplCopyWithImpl<
|
||||
_$HomeDownloaderUIStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _HomeDownloaderUIState implements HomeDownloaderUIState {
|
||||
factory _HomeDownloaderUIState(
|
||||
{final List<Aria2Task> tasks,
|
||||
final List<Aria2Task> waitingTasks,
|
||||
final List<Aria2Task> stoppedTasks,
|
||||
final Aria2GlobalStat? globalStat}) = _$HomeDownloaderUIStateImpl;
|
||||
|
||||
@override
|
||||
List<Aria2Task> get tasks;
|
||||
@override
|
||||
List<Aria2Task> get waitingTasks;
|
||||
@override
|
||||
List<Aria2Task> get stoppedTasks;
|
||||
@override
|
||||
Aria2GlobalStat? get globalStat;
|
||||
|
||||
/// Create a copy of HomeDownloaderUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$HomeDownloaderUIStateImplCopyWith<_$HomeDownloaderUIStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'home_downloader_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$homeDownloaderUIModelHash() =>
|
||||
r'ece2e6da4576b945ead5767aea2ccacf5e3e17aa';
|
||||
|
||||
/// See also [HomeDownloaderUIModel].
|
||||
@ProviderFor(HomeDownloaderUIModel)
|
||||
final homeDownloaderUIModelProvider = AutoDisposeNotifierProvider<
|
||||
HomeDownloaderUIModel, HomeDownloaderUIState>.internal(
|
||||
HomeDownloaderUIModel.new,
|
||||
name: r'homeDownloaderUIModelProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$homeDownloaderUIModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$HomeDownloaderUIModel = AutoDisposeNotifier<HomeDownloaderUIState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,305 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.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:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.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 'game_doctor_ui_model.dart';
|
||||
|
||||
class HomeGameDoctorUI extends HookConsumerWidget {
|
||||
const HomeGameDoctorUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(homeGameDoctorUIModelProvider);
|
||||
final homeState = ref.watch(homeUIModelProvider);
|
||||
final model = ref.read(homeGameDoctorUIModelProvider.notifier);
|
||||
|
||||
useEffect(() {
|
||||
AnalyticsApi.touch("auto_scan_issues");
|
||||
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
dPrint("HomeGameDoctorUI useEffect doCheck timeStamp === $timeStamp");
|
||||
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),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
for (final item in {
|
||||
"rsi_log": S.current.doctor_action_rsi_launcher_log,
|
||||
"game_log": S.current.doctor_action_game_run_log,
|
||||
}.entries)
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () =>
|
||||
_onTapButton(context, item.key, homeState)),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.isChecking)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(state.lastScreenInfo)
|
||||
],
|
||||
),
|
||||
))
|
||||
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),
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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);
|
||||
launchUrlString(
|
||||
"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,
|
||||
),
|
||||
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<Widget> 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),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ListView.builder(
|
||||
itemCount: state.checkResult!.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = state.checkResult![index];
|
||||
return makeResultItem(context, item, state, model);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 64),
|
||||
];
|
||||
}
|
||||
|
||||
Widget makeResultItem(BuildContext context, MapEntry<String, String> 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)),
|
||||
"nvme_PhysicalBytes": MapEntry(
|
||||
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),
|
||||
"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),
|
||||
"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)),
|
||||
};
|
||||
bool isCheckedError = errorNames.containsKey(item.key);
|
||||
|
||||
if (isCheckedError) {
|
||||
return Container(
|
||||
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),
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
child: Column(
|
||||
children: [
|
||||
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.withOpacity(.7)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: Button(
|
||||
onPressed: (errorNames[item.key]?.value == null || state.isFixing)
|
||||
? null
|
||||
: () async {
|
||||
await model.doFix(context, item);
|
||||
},
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text(S.current.doctor_info_action_fix),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final isSubTitleUrl = item.value.startsWith("https://");
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: ListTile(
|
||||
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.withOpacity(.7)),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: isSubTitleUrl
|
||||
? Button(
|
||||
onPressed: () {
|
||||
launchUrlString(item.value);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text(S.current.doctor_action_view_solution),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_onTapButton(
|
||||
BuildContext context, String key, HomeUIModelState homeState) async {
|
||||
switch (key) {
|
||||
case "rsi_log":
|
||||
final path = await SCLoggerHelper.getLogFilePath();
|
||||
if (path == null) return;
|
||||
SystemHelper.openDir(path);
|
||||
return;
|
||||
case "game_log":
|
||||
if (homeState.scInstalledPath == "not_install" ||
|
||||
homeState.scInstalledPath == null) {
|
||||
showToast(context, S.current.doctor_tip_title_select_game_directory);
|
||||
return;
|
||||
}
|
||||
SystemHelper.openDir("${homeState.scInstalledPath}\\Game.log");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,284 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
part 'game_doctor_ui_model.g.dart';
|
||||
|
||||
part 'game_doctor_ui_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class HomeGameDoctorState with _$HomeGameDoctorState {
|
||||
factory HomeGameDoctorState({
|
||||
@Default(false) bool isChecking,
|
||||
@Default(false) bool isFixing,
|
||||
@Default("") String lastScreenInfo,
|
||||
@Default("") String isFixingString,
|
||||
List<MapEntry<String, String>>? checkResult,
|
||||
}) = _HomeGameDoctorState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
|
||||
@override
|
||||
HomeGameDoctorState build() {
|
||||
state = HomeGameDoctorState();
|
||||
return state;
|
||||
}
|
||||
|
||||
Future<void> doFix(
|
||||
// ignore: avoid_build_context_in_providers
|
||||
BuildContext context,
|
||||
MapEntry<String, String> item) async {
|
||||
final checkResult =
|
||||
List<MapEntry<String, String>>.from(state.checkResult ?? []);
|
||||
state = state.copyWith(isFixing: true, isFixingString: "");
|
||||
switch (item.key) {
|
||||
case "unSupport_system":
|
||||
showToast(context, S.current.doctor_action_result_try_latest_windows);
|
||||
break;
|
||||
case "no_live_path":
|
||||
try {
|
||||
await Directory(item.value).create(recursive: true);
|
||||
if (!context.mounted) break;
|
||||
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));
|
||||
}
|
||||
break;
|
||||
case "nvme_PhysicalBytes":
|
||||
final r = await SystemHelper.addNvmePatch();
|
||||
if (r == "") {
|
||||
if (!context.mounted) break;
|
||||
showToast(context, S.current.doctor_action_result_fix_success);
|
||||
checkResult.remove(item);
|
||||
state = state.copyWith(checkResult: checkResult);
|
||||
} else {
|
||||
if (!context.mounted) break;
|
||||
showToast(context, S.current.doctor_action_result_fix_fail(r));
|
||||
}
|
||||
break;
|
||||
case "eac_file_miss":
|
||||
showToast(context,
|
||||
S.current.doctor_info_result_verify_files_with_rsi_launcher);
|
||||
break;
|
||||
case "eac_not_install":
|
||||
final eacJsonPath = "${item.value}\\Settings.json";
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
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]);
|
||||
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);
|
||||
checkResult.remove(item);
|
||||
state = state.copyWith(checkResult: checkResult);
|
||||
} else {
|
||||
if (!context.mounted) break;
|
||||
showToast(context,
|
||||
S.current.doctor_action_result_fix_fail(result.stderr));
|
||||
}
|
||||
} catch (e) {
|
||||
if (!context.mounted) break;
|
||||
showToast(context, S.current.doctor_action_result_fix_fail(e));
|
||||
}
|
||||
break;
|
||||
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");
|
||||
break;
|
||||
default:
|
||||
showToast(context, S.current.doctor_action_result_issue_not_supported);
|
||||
break;
|
||||
}
|
||||
state = state.copyWith(isFixing: false, isFixingString: "");
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
doCheck(BuildContext context) async {
|
||||
if (state.isChecking) return;
|
||||
state = state.copyWith(
|
||||
isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing);
|
||||
dPrint("-------- start docker check -----");
|
||||
if (!context.mounted) return;
|
||||
await _statCheck(context);
|
||||
state = state.copyWith(isChecking: false);
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
_statCheck(BuildContext context) async {
|
||||
final homeState = ref.read(homeUIModelProvider);
|
||||
final scInstalledPath = homeState.scInstalledPath!;
|
||||
|
||||
final checkResult = <MapEntry<String, String>>[];
|
||||
// TODO for debug
|
||||
// checkResult?.add(MapEntry("unSupport_system", "android"));
|
||||
// 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;
|
||||
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);
|
||||
}
|
||||
|
||||
if (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<MapEntry<String, String>> checkResult) async {
|
||||
if (scInstalledPath == "not_install") return;
|
||||
final lastScreenInfo = S.current.doctor_action_tip_checking_game_log;
|
||||
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||
final 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));
|
||||
} else {
|
||||
checkResult.add(MapEntry(
|
||||
S.current.doctor_action_info_game_abnormal_exit_unknown,
|
||||
S.current.doctor_action_info_info_feedback(info.value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future _checkEAC(BuildContext context, String scInstalledPath,
|
||||
List<MapEntry<String, String>> 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()) {
|
||||
checkResult.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||
final eacID = eacJson["productid"];
|
||||
final eacDeploymentId = eacJson["deploymentid"];
|
||||
if (eacID == null || eacDeploymentId == null) {
|
||||
checkResult.add(const MapEntry("eac_file_miss", ""));
|
||||
return;
|
||||
}
|
||||
final eacFilePath =
|
||||
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\anticheatlauncher.log";
|
||||
if (!await File(eacFilePath).exists()) {
|
||||
checkResult.add(MapEntry("eac_not_install", eacPath));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final _cnExp = RegExp(r"[^\x00-\xff]");
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future _checkPreInstall(BuildContext context, String scInstalledPath,
|
||||
List<MapEntry<String, String>> 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);
|
||||
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||
await showToast(context, lastScreenInfo);
|
||||
}
|
||||
|
||||
if (_cnExp.hasMatch(await SCLoggerHelper.getLogFilePath() ?? "")) {
|
||||
checkResult.add(const MapEntry("cn_user_name", ""));
|
||||
}
|
||||
|
||||
// 检查 RAM
|
||||
final ramSize = await SystemHelper.getSystemMemorySizeGB();
|
||||
if (ramSize < 16) {
|
||||
checkResult.add(MapEntry("low_ram", "$ramSize"));
|
||||
}
|
||||
state = state.copyWith(
|
||||
lastScreenInfo: S.current.doctor_action_info_checking_install_info);
|
||||
// 检查安装分区
|
||||
try {
|
||||
final listData = await SCLoggerHelper.getGameInstallPath(
|
||||
await SCLoggerHelper.getLauncherLogList() ?? []);
|
||||
final p = [];
|
||||
final checkedPath = [];
|
||||
for (var installPath in listData) {
|
||||
if (!checkedPath.contains(installPath)) {
|
||||
if (_cnExp.hasMatch(installPath)) {
|
||||
checkResult.add(MapEntry("cn_install_path", installPath));
|
||||
}
|
||||
if (scInstalledPath == "not_install") {
|
||||
checkedPath.add(installPath);
|
||||
if (!await Directory(installPath).exists()) {
|
||||
checkResult.add(MapEntry("no_live_path", installPath));
|
||||
}
|
||||
}
|
||||
final tp = installPath.split(":")[0];
|
||||
if (!p.contains(tp)) {
|
||||
p.add(tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call check
|
||||
for (var element in p) {
|
||||
var result = await Process.run('powershell', [
|
||||
"(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].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);
|
||||
if (physicalBytesPerSectorForPerformance > 4096) {
|
||||
checkResult.add(MapEntry("nvme_PhysicalBytes", element));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,253 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'game_doctor_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$HomeGameDoctorState {
|
||||
bool get isChecking => throw _privateConstructorUsedError;
|
||||
bool get isFixing => throw _privateConstructorUsedError;
|
||||
String get lastScreenInfo => throw _privateConstructorUsedError;
|
||||
String get isFixingString => throw _privateConstructorUsedError;
|
||||
List<MapEntry<String, String>>? get checkResult =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of HomeGameDoctorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HomeGameDoctorStateCopyWith<HomeGameDoctorState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $HomeGameDoctorStateCopyWith<$Res> {
|
||||
factory $HomeGameDoctorStateCopyWith(
|
||||
HomeGameDoctorState value, $Res Function(HomeGameDoctorState) then) =
|
||||
_$HomeGameDoctorStateCopyWithImpl<$Res, HomeGameDoctorState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isChecking,
|
||||
bool isFixing,
|
||||
String lastScreenInfo,
|
||||
String isFixingString,
|
||||
List<MapEntry<String, String>>? checkResult});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$HomeGameDoctorStateCopyWithImpl<$Res, $Val extends HomeGameDoctorState>
|
||||
implements $HomeGameDoctorStateCopyWith<$Res> {
|
||||
_$HomeGameDoctorStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// 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,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isChecking: null == isChecking
|
||||
? _value.isChecking
|
||||
: isChecking // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isFixing: null == isFixing
|
||||
? _value.isFixing
|
||||
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
lastScreenInfo: null == lastScreenInfo
|
||||
? _value.lastScreenInfo
|
||||
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isFixingString: null == isFixingString
|
||||
? _value.isFixingString
|
||||
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
checkResult: freezed == checkResult
|
||||
? _value.checkResult
|
||||
: checkResult // ignore: cast_nullable_to_non_nullable
|
||||
as List<MapEntry<String, String>>?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$HomeGameDoctorStateImplCopyWith<$Res>
|
||||
implements $HomeGameDoctorStateCopyWith<$Res> {
|
||||
factory _$$HomeGameDoctorStateImplCopyWith(_$HomeGameDoctorStateImpl value,
|
||||
$Res Function(_$HomeGameDoctorStateImpl) then) =
|
||||
__$$HomeGameDoctorStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isChecking,
|
||||
bool isFixing,
|
||||
String lastScreenInfo,
|
||||
String isFixingString,
|
||||
List<MapEntry<String, String>>? checkResult});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$HomeGameDoctorStateImplCopyWithImpl<$Res>
|
||||
extends _$HomeGameDoctorStateCopyWithImpl<$Res, _$HomeGameDoctorStateImpl>
|
||||
implements _$$HomeGameDoctorStateImplCopyWith<$Res> {
|
||||
__$$HomeGameDoctorStateImplCopyWithImpl(_$HomeGameDoctorStateImpl _value,
|
||||
$Res Function(_$HomeGameDoctorStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// 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,
|
||||
}) {
|
||||
return _then(_$HomeGameDoctorStateImpl(
|
||||
isChecking: null == isChecking
|
||||
? _value.isChecking
|
||||
: isChecking // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isFixing: null == isFixing
|
||||
? _value.isFixing
|
||||
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
lastScreenInfo: null == lastScreenInfo
|
||||
? _value.lastScreenInfo
|
||||
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isFixingString: null == isFixingString
|
||||
? _value.isFixingString
|
||||
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
checkResult: freezed == checkResult
|
||||
? _value._checkResult
|
||||
: checkResult // ignore: cast_nullable_to_non_nullable
|
||||
as List<MapEntry<String, String>>?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$HomeGameDoctorStateImpl implements _HomeGameDoctorState {
|
||||
_$HomeGameDoctorStateImpl(
|
||||
{this.isChecking = false,
|
||||
this.isFixing = false,
|
||||
this.lastScreenInfo = "",
|
||||
this.isFixingString = "",
|
||||
final List<MapEntry<String, String>>? checkResult})
|
||||
: _checkResult = checkResult;
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isChecking;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFixing;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String lastScreenInfo;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String isFixingString;
|
||||
final List<MapEntry<String, String>>? _checkResult;
|
||||
@override
|
||||
List<MapEntry<String, String>>? get checkResult {
|
||||
final value = _checkResult;
|
||||
if (value == null) return null;
|
||||
if (_checkResult is EqualUnmodifiableListView) return _checkResult;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$HomeGameDoctorStateImpl &&
|
||||
(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));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
isChecking,
|
||||
isFixing,
|
||||
lastScreenInfo,
|
||||
isFixingString,
|
||||
const DeepCollectionEquality().hash(_checkResult));
|
||||
|
||||
/// Create a copy of HomeGameDoctorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$HomeGameDoctorStateImplCopyWith<_$HomeGameDoctorStateImpl> get copyWith =>
|
||||
__$$HomeGameDoctorStateImplCopyWithImpl<_$HomeGameDoctorStateImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _HomeGameDoctorState implements HomeGameDoctorState {
|
||||
factory _HomeGameDoctorState(
|
||||
{final bool isChecking,
|
||||
final bool isFixing,
|
||||
final String lastScreenInfo,
|
||||
final String isFixingString,
|
||||
final List<MapEntry<String, String>>? checkResult}) =
|
||||
_$HomeGameDoctorStateImpl;
|
||||
|
||||
@override
|
||||
bool get isChecking;
|
||||
@override
|
||||
bool get isFixing;
|
||||
@override
|
||||
String get lastScreenInfo;
|
||||
@override
|
||||
String get isFixingString;
|
||||
@override
|
||||
List<MapEntry<String, String>>? get checkResult;
|
||||
|
||||
/// Create a copy of HomeGameDoctorState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$HomeGameDoctorStateImplCopyWith<_$HomeGameDoctorStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'game_doctor_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$homeGameDoctorUIModelHash() =>
|
||||
r'b69a19a937ca375214a7c7e73b8288f577265625';
|
||||
|
||||
/// See also [HomeGameDoctorUIModel].
|
||||
@ProviderFor(HomeGameDoctorUIModel)
|
||||
final homeGameDoctorUIModelProvider = AutoDisposeNotifierProvider<
|
||||
HomeGameDoctorUIModel, HomeGameDoctorState>.internal(
|
||||
HomeGameDoctorUIModel.new,
|
||||
name: r'homeGameDoctorUIModelProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$homeGameDoctorUIModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$HomeGameDoctorUIModel = AutoDisposeNotifier<HomeGameDoctorState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -7,8 +7,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.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';
|
||||
|
||||
@ -117,76 +115,6 @@ class HomeUI extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current.home_install_location),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: ComboBox<String>(
|
||||
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)],
|
||||
),
|
||||
)
|
||||
],
|
||||
onChanged: model.onChangeInstallPath,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: homeState.webLocalizationVersionsData == null
|
||||
? null
|
||||
: () => model.launchRSI(context),
|
||||
style: homeState.isCurGameRunning
|
||||
? null
|
||||
: ButtonStyle(
|
||||
backgroundColor:
|
||||
WidgetStateProperty.resolveWith(_getRunButtonColor),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Icon(
|
||||
homeState.isCurGameRunning
|
||||
? FluentIcons.stop_solid
|
||||
: FluentIcons.play_solid,
|
||||
color: homeState.isCurGameRunning
|
||||
? Colors.red.withOpacity(.8)
|
||||
: Colors.white,
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: () =>
|
||||
SystemHelper.openDir("${homeState.scInstalledPath}"),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.folder_open),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: model.reScanPath,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.refresh),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(homeState.lastScreenInfo, maxLines: 1),
|
||||
makeIndexActionLists(context, model, homeState, ref),
|
||||
@ -796,7 +724,8 @@ class HomeUI extends HookConsumerWidget {
|
||||
switch (key) {
|
||||
case "localization":
|
||||
if (homeState.scInstalledPath == "not_install") {
|
||||
ToolsUIModel.rsiEnhance(context, showNotGameInstallMsg: true);
|
||||
// TODO
|
||||
// ToolsUIModel.English(context, showNotGameInstallMsg: true);
|
||||
break;
|
||||
}
|
||||
final model = ref.watch(homeUIModelProvider.notifier);
|
||||
|
@ -1,22 +1,15 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_rss/domain/rss_item.dart';
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.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/api/rss.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.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/win32_api.dart' as win32;
|
||||
import 'package:starcitizen_doctor/common/utils/async.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
@ -24,12 +17,10 @@ import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/app_placard_data.dart';
|
||||
import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart';
|
||||
import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/dialogs/home_game_login_dialog_ui.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:html/parser.dart' as html;
|
||||
import 'package:html/dom.dart' as html_dom;
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import '../webview/webview.dart';
|
||||
import 'localization/localization_ui_model.dart';
|
||||
|
||||
part 'home_ui_model.freezed.dart';
|
||||
@ -76,40 +67,7 @@ class HomeUIModel extends _$HomeUIModel {
|
||||
}
|
||||
|
||||
Future<void> reScanPath() async {
|
||||
state = state.copyWith(
|
||||
scInstalledPath: "not_install",
|
||||
lastScreenInfo: S.current.home_action_info_scanning);
|
||||
try {
|
||||
final listData = await SCLoggerHelper.getLauncherLogList();
|
||||
if (listData == null) {
|
||||
state = state.copyWith(scInstalledPath: "not_install");
|
||||
return;
|
||||
}
|
||||
final scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
|
||||
withVersion: ["LIVE", "PTU", "EPTU"], checkExists: true);
|
||||
|
||||
String scInstalledPath = "not_install";
|
||||
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
if (scInstallPaths.first.isNotEmpty) {
|
||||
scInstalledPath = scInstallPaths.first;
|
||||
}
|
||||
}
|
||||
final lastScreenInfo = S.current
|
||||
.home_action_info_scan_complete_valid_directories_found(
|
||||
scInstallPaths.length.toString());
|
||||
state = state.copyWith(
|
||||
scInstalledPath: scInstalledPath,
|
||||
scInstallPaths: scInstallPaths,
|
||||
lastScreenInfo: lastScreenInfo);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
scInstalledPath: "not_install",
|
||||
lastScreenInfo: S.current.home_action_info_log_file_parse_fail);
|
||||
AnalyticsApi.touch("error_launchLogs");
|
||||
// showToast(context!,
|
||||
// "${S.current.home_action_info_log_file_parse_fail} \n请关闭游戏,退出RSI启动器后重试,若仍有问题,请使用工具箱中的 RSI Launcher log 修复。");
|
||||
}
|
||||
state = state.copyWith(scInstalledPath: "not_install", lastScreenInfo: "");
|
||||
}
|
||||
|
||||
String getRssImage(RssItem item) {
|
||||
@ -134,65 +92,8 @@ class HomeUIModel extends _$HomeUIModel {
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<void> goWebView(BuildContext context, String title, String url,
|
||||
{bool useLocalization = false,
|
||||
bool loginMode = false,
|
||||
RsiLoginCallback? rsiLoginCallback}) async {
|
||||
if (useLocalization) {
|
||||
const tipVersion = 2;
|
||||
final box = await Hive.openBox("app_conf");
|
||||
final skip =
|
||||
await box.get("skip_web_localization_tip_version", defaultValue: 0);
|
||||
if (skip != tipVersion) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.home_action_title_star_citizen_website_localization,
|
||||
Text(
|
||||
S.current.home_action_info_web_localization_plugin_disclaimer,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .6));
|
||||
if (!ok) {
|
||||
if (loginMode) {
|
||||
rsiLoginCallback?.call(null, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await box.put("skip_web_localization_tip_version", tipVersion);
|
||||
}
|
||||
}
|
||||
if (!await WebviewWindow.isWebviewAvailable()) {
|
||||
if (!context.mounted) return;
|
||||
showToast(
|
||||
context, S.current.home_login_action_title_need_webview2_runtime);
|
||||
launchUrlString(
|
||||
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final webViewModel = WebViewModel(context,
|
||||
loginMode: loginMode, loginCallback: rsiLoginCallback);
|
||||
if (useLocalization) {
|
||||
state = state.copyWith(
|
||||
isFixing: true,
|
||||
isFixingString: S.current.home_action_info_initializing_resources);
|
||||
try {
|
||||
await webViewModel.initLocalization(state.webLocalizationVersionsData!);
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.home_action_info_initialization_failed(e));
|
||||
}
|
||||
state = state.copyWith(isFixingString: "", isFixing: false);
|
||||
}
|
||||
await webViewModel.initWebView(
|
||||
title: title,
|
||||
applicationSupportDir: appGlobalState.applicationSupportDir!,
|
||||
appVersionData: appGlobalState.networkVersionData!,
|
||||
);
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await webViewModel.launch(url, appGlobalState.networkVersionData!);
|
||||
{bool useLocalization = false, bool loginMode = false}) async {
|
||||
launchUrlString(url);
|
||||
}
|
||||
|
||||
bool isRSIServerStatusOK(Map map) {
|
||||
@ -277,28 +178,9 @@ class HomeUIModel extends _$HomeUIModel {
|
||||
|
||||
Future<void> checkLocalizationUpdate({bool skipReload = false}) async {
|
||||
dPrint("_checkLocalizationUpdate");
|
||||
final updates = await (ref.read(localizationUIModelProvider.notifier))
|
||||
await (ref.read(localizationUIModelProvider.notifier))
|
||||
.checkLangUpdate(skipReload: skipReload)
|
||||
.unwrap<List<String>>();
|
||||
if (updates == null || updates.isEmpty) {
|
||||
state = state.copyWith(localizationUpdateInfo: null);
|
||||
return;
|
||||
}
|
||||
state =
|
||||
state.copyWith(localizationUpdateInfo: MapEntry(updates.first, true));
|
||||
if (_appUpdateTimer != null) {
|
||||
_appUpdateTimer?.cancel();
|
||||
_appUpdateTimer = null;
|
||||
// 发送通知
|
||||
await win32.sendNotify(
|
||||
summary: S.current.home_localization_new_version_available,
|
||||
body:
|
||||
S.current.home_localization_new_version_installed(updates.first),
|
||||
appName: S.current.home_title_app_name,
|
||||
appId: ConstConf.isMSE
|
||||
? "56575xkeyC.MSE_bsn1nexg8e4qe!starcitizendoctor"
|
||||
: "{6D809377-6AF0-444B-8957-A3773F02200E}\\Starcitizen_Doctor\\starcitizen_doctor.exe");
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: avoid_build_context_in_providers
|
||||
@ -307,109 +189,10 @@ class HomeUIModel extends _$HomeUIModel {
|
||||
showToast(context, S.current.home_info_valid_installation_required);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConstConf.isMSE) {
|
||||
if (state.isCurGameRunning) {
|
||||
await Process.run(
|
||||
SystemHelper.powershellPath, ["ps \"StarCitizen\" | kill"]);
|
||||
return;
|
||||
}
|
||||
AnalyticsApi.touch("gameLaunch");
|
||||
showDialog(
|
||||
context: context,
|
||||
dismissWithEsc: false,
|
||||
builder: (context) => HomeGameLoginDialogUI(context));
|
||||
} else {
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.home_info_one_click_launch_warning,
|
||||
Text(S.current.home_info_account_security_warning),
|
||||
confirm: S.current.home_action_install_microsoft_store_version,
|
||||
cancel: S.current.home_action_cancel);
|
||||
if (ok == true) {
|
||||
await launchUrlString(
|
||||
"https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true");
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onChangeInstallPath(String? value) {
|
||||
if (value == null) return;
|
||||
state = state.copyWith(scInstalledPath: value);
|
||||
}
|
||||
|
||||
doLaunchGame(
|
||||
// ignore: avoid_build_context_in_providers
|
||||
BuildContext context,
|
||||
String launchExe,
|
||||
List<String> args,
|
||||
String installPath,
|
||||
String? processorAffinity) async {
|
||||
var runningMap = Map<String, bool>.from(state.isGameRunning);
|
||||
runningMap[installPath] = true;
|
||||
state = state.copyWith(isGameRunning: runningMap);
|
||||
try {
|
||||
late ProcessResult result;
|
||||
if (processorAffinity == null) {
|
||||
result = await Process.run(launchExe, args);
|
||||
} else {
|
||||
dPrint("set Affinity === $processorAffinity launchExe === $launchExe");
|
||||
result = await Process.run("cmd.exe", [
|
||||
'/C',
|
||||
'Start',
|
||||
'"StarCitizen"',
|
||||
'/High',
|
||||
'/Affinity',
|
||||
processorAffinity,
|
||||
launchExe,
|
||||
...args
|
||||
]);
|
||||
}
|
||||
dPrint('Exit code: ${result.exitCode}');
|
||||
dPrint('stdout: ${result.stdout}');
|
||||
dPrint('stderr: ${result.stderr}');
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
final logs = await SCLoggerHelper.getGameRunningLogs(installPath);
|
||||
MapEntry<String, String>? exitInfo;
|
||||
bool hasUrl = false;
|
||||
if (logs != null) {
|
||||
exitInfo = SCLoggerHelper.getGameRunningLogInfo(logs);
|
||||
if (exitInfo!.value.startsWith("https://")) {
|
||||
hasUrl = true;
|
||||
}
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
// showToast(context,
|
||||
// "游戏非正常退出\nexitCode=${result.exitCode}\nstdout=${result.stdout ?? ""}\nstderr=${result.stderr ?? ""}\n\n诊断信息:${exitInfo == null ? "未知错误,请通过一键诊断加群反馈。" : exitInfo.key} \n${hasUrl ? "请查看弹出的网页链接获得详细信息。" : exitInfo?.value ?? ""}");
|
||||
// S.current.home_action_info_abnormal_game_exit
|
||||
showToast(
|
||||
context,
|
||||
S.current.home_action_info_abnormal_game_exit(
|
||||
result.exitCode.toString(),
|
||||
result.stdout ?? "",
|
||||
result.stderr ?? "",
|
||||
exitInfo == null
|
||||
? S.current.home_action_info_unknown_error
|
||||
: exitInfo.key,
|
||||
hasUrl
|
||||
? S.current.home_action_info_check_web_link
|
||||
: exitInfo?.value ?? ""));
|
||||
if (hasUrl) {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
launchUrlString(exitInfo!.value);
|
||||
}
|
||||
}
|
||||
|
||||
final launchFile = File("$installPath\\loginData.json");
|
||||
if (await launchFile.exists()) {
|
||||
await launchFile.delete();
|
||||
}
|
||||
} catch (_) {}
|
||||
runningMap = Map<String, bool>.from(state.isGameRunning);
|
||||
runningMap[installPath] = false;
|
||||
state = state.copyWith(isGameRunning: runningMap);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ part of 'home_ui_model.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$homeUIModelHash() => r'85d3242abb4264a814768a2d5ce108df46df38d9';
|
||||
String _$homeUIModelHash() => r'6a768281606856766737a63aaeebb392c4613d2b';
|
||||
|
||||
/// See also [HomeUIModel].
|
||||
@ProviderFor(HomeUIModel)
|
||||
|
@ -10,7 +10,6 @@ import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/localization/advanced_localization_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/unp4kc/unp4kc_ui.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
|
||||
@ -58,7 +57,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
|
||||
: Column(
|
||||
children: [
|
||||
if (state.errorMessage.isNotEmpty)
|
||||
UnP4kErrorWidget(
|
||||
ErrorMessageWidget(
|
||||
errorMessage: state.errorMessage,
|
||||
)
|
||||
else ...[
|
||||
@ -313,3 +312,32 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorMessageWidget extends HookConsumerWidget {
|
||||
final String errorMessage;
|
||||
|
||||
const ErrorMessageWidget({super.key, required this.errorMessage});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(.1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.error),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
errorMessage,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,8 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/provider/unp4kc.dart';
|
||||
|
||||
import '../home_ui_model.dart';
|
||||
import 'advanced_localization_ui.json.dart';
|
||||
@ -217,32 +215,7 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
|
||||
}
|
||||
|
||||
Future<String> readEnglishInI(String gameDir) async {
|
||||
try {
|
||||
var data = await Unp4kCModel.unp4kTools(
|
||||
appGlobalState.applicationBinaryModuleDir!, [
|
||||
"extract_memory",
|
||||
"$gameDir\\Data.p4k",
|
||||
"Data\\Localization\\english\\global.ini"
|
||||
]);
|
||||
// remove bom
|
||||
if (data.length > 3 &&
|
||||
data[0] == 0xEF &&
|
||||
data[1] == 0xBB &&
|
||||
data[2] == 0xBF) {
|
||||
data = data.sublist(3);
|
||||
}
|
||||
final iniData = String.fromCharCodes(data);
|
||||
return iniData;
|
||||
} catch (e) {
|
||||
final errorMessage = e.toString();
|
||||
if (Unp4kCModel.checkRunTimeError(errorMessage)) {
|
||||
AnalyticsApi.touch("advanced_localization_no_runtime");
|
||||
}
|
||||
state = state.copyWith(
|
||||
errorMessage: errorMessage,
|
||||
);
|
||||
// rethrow;
|
||||
}
|
||||
// TODO read English p4kGlobalIni
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$advancedLocalizationUIModelHash() =>
|
||||
r'8241143c6dec93cd705e6b2e65cbca711cdfe2fb';
|
||||
r'60ccd50f54b948d16be001f5ea07972a0fd9ed3f';
|
||||
|
||||
/// See also [AdvancedLocalizationUIModel].
|
||||
@ProviderFor(AdvancedLocalizationUIModel)
|
||||
|
@ -5,7 +5,6 @@ import 'package:flutter_tilt/flutter_tilt.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
import 'localization_form_file_dialog_ui.dart';
|
||||
@ -495,7 +494,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
? () async {
|
||||
switch (item.key) {
|
||||
case "launcher_mod":
|
||||
ToolsUIModel.rsiEnhance(context);
|
||||
// ToolsUIModel.rsiEnhance(context);
|
||||
break;
|
||||
case "advanced":
|
||||
context.push("/index/advanced_localization");
|
||||
|
@ -6,8 +6,6 @@ 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/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/data/game_performance_data.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
@ -165,20 +163,20 @@ class HomePerformanceUIModel extends _$HomePerformanceUIModel {
|
||||
}
|
||||
|
||||
cleanShaderCache(BuildContext? context) async {
|
||||
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
final l =
|
||||
await Directory(gameShaderCachePath!).list(recursive: false).toList();
|
||||
for (var value in l) {
|
||||
if (value is Directory) {
|
||||
if (!value.absolute.path.contains("Crashes")) {
|
||||
await value.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
if (context != null && context.mounted) {
|
||||
showToast(context, S.current.performance_info_shader_clearing_warning);
|
||||
}
|
||||
// final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
// final l =
|
||||
// await Directory(gameShaderCachePath!).list(recursive: false).toList();
|
||||
// for (var value in l) {
|
||||
// if (value is Directory) {
|
||||
// if (!value.absolute.path.contains("Crashes")) {
|
||||
// await value.delete(recursive: true);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// await Future.delayed(const Duration(milliseconds: 300));
|
||||
// if (context != null && context.mounted) {
|
||||
// showToast(context, S.current.performance_info_shader_clearing_warning);
|
||||
// }
|
||||
}
|
||||
|
||||
applyProfile(bool cleanShader) async {
|
||||
|
@ -7,7 +7,7 @@ part of 'performance_ui_model.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$homePerformanceUIModelHash() =>
|
||||
r'83fbdbbae287892dd0c67f5fd86d42a73d0ab91f';
|
||||
r'a33d8c621f4cd150b1a091bfd0243d578f4a705c';
|
||||
|
||||
/// See also [HomePerformanceUIModel].
|
||||
@ProviderFor(HomePerformanceUIModel)
|
||||
|
@ -1,19 +1,15 @@
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/settings/settings_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/src/blur_oval_widget.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'about/about_ui.dart';
|
||||
import 'home/home_ui.dart';
|
||||
import 'settings/settings_ui.dart';
|
||||
import 'tools/tools_ui.dart';
|
||||
|
||||
class IndexUI extends HookConsumerWidget {
|
||||
const IndexUI({super.key});
|
||||
@ -22,67 +18,65 @@ class IndexUI extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// pre init child
|
||||
ref.watch(homeUIModelProvider.select((value) => null));
|
||||
ref.watch(settingsUIModelProvider.select((value) => null));
|
||||
ref.watch(appGlobalModelProvider);
|
||||
// ref.watch(settingsUIModelProvider.select((value) => null));
|
||||
final globalState = ref.watch(appGlobalModelProvider);
|
||||
|
||||
final curIndex = useState(0);
|
||||
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")),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}(),
|
||||
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.withOpacity(.6),
|
||||
),
|
||||
),
|
||||
_makeAria2TaskNumWidget()
|
||||
],
|
||||
),
|
||||
onPressed: () => _goDownloader(context),
|
||||
// onPressed: model.goDownloader
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
const WindowButtons()
|
||||
],
|
||||
)),
|
||||
pane: NavigationPane(
|
||||
key: Key("NavigationPane_${S.current.app_language_code}"),
|
||||
selected: curIndex.value,
|
||||
items: getNavigationPaneItems(curIndex),
|
||||
size: NavigationPaneSize(
|
||||
openWidth: S.current.app_language_code.startsWith("zh") ? 64 : 74),
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image:
|
||||
ExtendedAssetImageProvider(globalState.backgroundImageAssetsPath),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: BlurOvalWidget(
|
||||
child: Container(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 1440, maxHeight: 1000),
|
||||
child: NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: () {
|
||||
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,
|
||||
ConstConf.isMSE ? "" : " Dev")),
|
||||
],
|
||||
),
|
||||
);
|
||||
}(),
|
||||
),
|
||||
pane: NavigationPane(
|
||||
key: Key("NavigationPane_${S.current.app_language_code}"),
|
||||
selected: curIndex.value,
|
||||
items: getNavigationPaneItems(curIndex),
|
||||
size: NavigationPaneSize(
|
||||
openWidth: S.current.app_language_code.startsWith("zh")
|
||||
? 64
|
||||
: 74),
|
||||
),
|
||||
paneBodyBuilder: (item, child) {
|
||||
return item!.body;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
paneBodyBuilder: (item, child) {
|
||||
return item!.body;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -93,11 +87,11 @@ class IndexUI extends HookConsumerWidget {
|
||||
),
|
||||
FluentIcons.toolbox: (
|
||||
S.current.app_index_menu_tools,
|
||||
const ToolsUI(),
|
||||
const SizedBox(),
|
||||
),
|
||||
FluentIcons.settings: (
|
||||
S.current.app_index_menu_settings,
|
||||
const SettingsUI()
|
||||
const SizedBox()
|
||||
),
|
||||
FluentIcons.info: (
|
||||
S.current.app_index_menu_about,
|
||||
@ -140,37 +134,4 @@ class IndexUI extends HookConsumerWidget {
|
||||
pageMenus.values.toList().indexWhere((element) => element.$1 == value);
|
||||
curIndexState.value = pageIndex;
|
||||
}
|
||||
|
||||
Widget _makeAria2TaskNumWidget() {
|
||||
return Consumer(
|
||||
builder: (BuildContext context, WidgetRef ref, Widget? child) {
|
||||
final aria2cState = ref.watch(aria2cModelProvider);
|
||||
if (!aria2cState.hasDownloadTask) {
|
||||
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,
|
||||
),
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_goDownloader(BuildContext context) {
|
||||
context.push('/index/downloader');
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class PartyRoomUI extends HookConsumerWidget {
|
||||
const PartyRoomUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
S.current.lobby_online_lobby_coming_soon,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString("https://wj.qq.com/s2/14112124/f4c8/");
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current.lobby_invitation_to_participate),
|
||||
Text(
|
||||
S.current.lobby_survey,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:starcitizen_doctor/ui/settings/settings_ui_model.dart';
|
||||
|
||||
class SettingsUI extends HookConsumerWidget {
|
||||
const SettingsUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sate = ref.watch(settingsUIModelProvider);
|
||||
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: [
|
||||
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.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),
|
||||
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),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget makeSettingsItem(
|
||||
Widget icon,
|
||||
String title, {
|
||||
String? subTitle,
|
||||
VoidCallback? onTap,
|
||||
VoidCallback? onDel,
|
||||
void Function(bool? b)? onSwitch,
|
||||
bool switchStatus = false,
|
||||
bool showGoIcon = true,
|
||||
Map<dynamic, String> comboMenus = const {},
|
||||
ValueChanged? onComboChanged,
|
||||
dynamic selectedComboValue,
|
||||
}) {
|
||||
return Button(
|
||||
onPressed: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 18),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(title),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
if (subTitle != null) ...[
|
||||
const SizedBox(height: 3),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: Text(
|
||||
subTitle,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: Colors.white.withOpacity(.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),
|
||||
],
|
||||
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),
|
||||
)
|
||||
],
|
||||
onChanged: onComboChanged,
|
||||
),
|
||||
)
|
||||
],
|
||||
const SizedBox(width: 12),
|
||||
if (showGoIcon) const Icon(FluentIcons.chevron_right),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
part 'settings_ui_model.g.dart';
|
||||
|
||||
part 'settings_ui_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class SettingsUIState with _$SettingsUIState {
|
||||
factory SettingsUIState({
|
||||
@Default(false) bool isEnableToolSiteMirrors,
|
||||
@Default("0") String inputGameLaunchECore,
|
||||
String? customLauncherPath,
|
||||
String? customGamePath,
|
||||
@Default(0) int locationCacheSize,
|
||||
}) = _SettingsUIState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SettingsUIModel extends _$SettingsUIModel {
|
||||
@override
|
||||
SettingsUIState build() {
|
||||
state = SettingsUIState();
|
||||
_initState();
|
||||
return state;
|
||||
}
|
||||
|
||||
void _initState() async {
|
||||
_updateGameLaunchECore();
|
||||
_loadCustomPath();
|
||||
_loadLocationCacheSize();
|
||||
_loadToolSiteMirrorState();
|
||||
}
|
||||
|
||||
Future<void> setGameLaunchECore(BuildContext context) async {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
final defaultInput =
|
||||
userBox.get("gameLaunch_eCore_count", defaultValue: "0");
|
||||
if (!context.mounted) return;
|
||||
final input = await showInputDialogs(context,
|
||||
title: S.current.setting_action_info_enter_cpu_core_to_ignore,
|
||||
content: S.current.setting_action_info_cpu_core_tip,
|
||||
initialValue: defaultInput,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly]);
|
||||
if (input == null) return;
|
||||
userBox.put("gameLaunch_eCore_count", input);
|
||||
_initState();
|
||||
}
|
||||
|
||||
Future _updateGameLaunchECore() async {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
final inputGameLaunchECore =
|
||||
userBox.get("gameLaunch_eCore_count", defaultValue: "0");
|
||||
state = state.copyWith(inputGameLaunchECore: inputGameLaunchECore);
|
||||
}
|
||||
|
||||
Future<void> setLauncherPath(BuildContext context) async {
|
||||
final r = await FilePicker.platform.pickFiles(
|
||||
dialogTitle: S.current.setting_action_info_select_rsi_launcher_location,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ["exe"]);
|
||||
if (r == null || r.files.firstOrNull?.path == null) return;
|
||||
final fileName = r.files.first.path!;
|
||||
if (fileName.endsWith("\\RSI Launcher.exe")) {
|
||||
await _saveCustomPath("custom_launcher_path", fileName);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.setting_action_info_setting_success);
|
||||
_initState();
|
||||
} else {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.setting_action_info_file_error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setGamePath(BuildContext context) async {
|
||||
final r = await FilePicker.platform.pickFiles(
|
||||
dialogTitle: S.current.setting_action_info_select_game_install_location,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ["exe"]);
|
||||
if (r == null || r.files.firstOrNull?.path == null) return;
|
||||
final fileName = r.files.first.path!;
|
||||
dPrint(fileName);
|
||||
final fileNameRegExp =
|
||||
RegExp(r"^(.*\\StarCitizen\\.*\\)Bin64\\StarCitizen\.exe$");
|
||||
if (fileNameRegExp.hasMatch(fileName)) {
|
||||
RegExp pathRegex = RegExp(r"\\[^\\]+\\Bin64\\StarCitizen\.exe$");
|
||||
String extractedPath = fileName.replaceFirst(pathRegex, '');
|
||||
await _saveCustomPath("custom_game_path", extractedPath);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.setting_action_info_setting_success);
|
||||
_initState();
|
||||
} else {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.setting_action_info_file_error);
|
||||
}
|
||||
}
|
||||
|
||||
_saveCustomPath(String pathKey, String dir) async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
await confBox.put(pathKey, dir);
|
||||
}
|
||||
|
||||
_loadCustomPath() async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final customLauncherPath = confBox.get("custom_launcher_path");
|
||||
final customGamePath = confBox.get("custom_game_path");
|
||||
state = state.copyWith(
|
||||
customLauncherPath: customLauncherPath, customGamePath: customGamePath);
|
||||
}
|
||||
|
||||
Future<void> delName(String key) async {
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
await confBox.delete(key);
|
||||
_initState();
|
||||
}
|
||||
|
||||
_loadLocationCacheSize() async {
|
||||
final len = await SystemHelper.getDirLen(
|
||||
"${appGlobalState.applicationSupportDir}/Localizations");
|
||||
final locationCacheSize = len;
|
||||
state = state.copyWith(locationCacheSize: locationCacheSize);
|
||||
}
|
||||
|
||||
Future<void> cleanLocationCache(BuildContext context) async {
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.setting_action_info_confirm_clear_cache,
|
||||
Text(S.current.setting_action_info_clear_cache_warning));
|
||||
if (ok == true) {
|
||||
final dir =
|
||||
Directory("${appGlobalState.applicationSupportDir}/Localizations");
|
||||
if (!context.mounted) return;
|
||||
await dir.delete(recursive: true).unwrap(context: context);
|
||||
_initState();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addShortCut(BuildContext context) async {
|
||||
if (ConstConf.isMSE) {
|
||||
showToast(
|
||||
context, S.current.setting_action_info_microsoft_version_limitation);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
Process.run("explorer.exe", ["shell:AppsFolder"]);
|
||||
return;
|
||||
}
|
||||
dPrint(Platform.resolvedExecutable);
|
||||
final shortCuntName = S.current.app_shortcut_name;
|
||||
final script = """
|
||||
\$targetPath = "${Platform.resolvedExecutable}";
|
||||
\$shortcutPath = [System.IO.Path]::Combine([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::DesktopDirectory), "$shortCuntName");
|
||||
\$shell = New-Object -ComObject WScript.Shell
|
||||
\$shortcut = \$shell.CreateShortcut(\$shortcutPath)
|
||||
if (\$shortcut -eq \$null) {
|
||||
Write-Host "Failed to create shortcut."
|
||||
} else {
|
||||
\$shortcut.TargetPath = \$targetPath
|
||||
\$shortcut.Save()
|
||||
Write-Host "Shortcut created successfully."
|
||||
}
|
||||
""";
|
||||
await Process.run(SystemHelper.powershellPath, [script]);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.setting_action_info_shortcut_created);
|
||||
}
|
||||
|
||||
_loadToolSiteMirrorState() async {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
final isEnableToolSiteMirrors =
|
||||
userBox.get("isEnableToolSiteMirrors", defaultValue: false);
|
||||
state = state.copyWith(isEnableToolSiteMirrors: isEnableToolSiteMirrors);
|
||||
}
|
||||
|
||||
void onChangeToolSiteMirror(bool? b) async {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
final isEnableToolSiteMirrors = b == true;
|
||||
await userBox.put("isEnableToolSiteMirrors", isEnableToolSiteMirrors);
|
||||
_initState();
|
||||
}
|
||||
|
||||
showLogs() async {
|
||||
SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"),
|
||||
isFile: true);
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'settings_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SettingsUIState {
|
||||
bool get isEnableToolSiteMirrors => throw _privateConstructorUsedError;
|
||||
String get inputGameLaunchECore => throw _privateConstructorUsedError;
|
||||
String? get customLauncherPath => throw _privateConstructorUsedError;
|
||||
String? get customGamePath => throw _privateConstructorUsedError;
|
||||
int get locationCacheSize => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of SettingsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$SettingsUIStateCopyWith<SettingsUIState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SettingsUIStateCopyWith<$Res> {
|
||||
factory $SettingsUIStateCopyWith(
|
||||
SettingsUIState value, $Res Function(SettingsUIState) then) =
|
||||
_$SettingsUIStateCopyWithImpl<$Res, SettingsUIState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isEnableToolSiteMirrors,
|
||||
String inputGameLaunchECore,
|
||||
String? customLauncherPath,
|
||||
String? customGamePath,
|
||||
int locationCacheSize});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SettingsUIStateCopyWithImpl<$Res, $Val extends SettingsUIState>
|
||||
implements $SettingsUIStateCopyWith<$Res> {
|
||||
_$SettingsUIStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SettingsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isEnableToolSiteMirrors = null,
|
||||
Object? inputGameLaunchECore = null,
|
||||
Object? customLauncherPath = freezed,
|
||||
Object? customGamePath = freezed,
|
||||
Object? locationCacheSize = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
|
||||
? _value.isEnableToolSiteMirrors
|
||||
: isEnableToolSiteMirrors // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
inputGameLaunchECore: null == inputGameLaunchECore
|
||||
? _value.inputGameLaunchECore
|
||||
: inputGameLaunchECore // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
customLauncherPath: freezed == customLauncherPath
|
||||
? _value.customLauncherPath
|
||||
: customLauncherPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
customGamePath: freezed == customGamePath
|
||||
? _value.customGamePath
|
||||
: customGamePath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationCacheSize: null == locationCacheSize
|
||||
? _value.locationCacheSize
|
||||
: locationCacheSize // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SettingsUIStateImplCopyWith<$Res>
|
||||
implements $SettingsUIStateCopyWith<$Res> {
|
||||
factory _$$SettingsUIStateImplCopyWith(_$SettingsUIStateImpl value,
|
||||
$Res Function(_$SettingsUIStateImpl) then) =
|
||||
__$$SettingsUIStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isEnableToolSiteMirrors,
|
||||
String inputGameLaunchECore,
|
||||
String? customLauncherPath,
|
||||
String? customGamePath,
|
||||
int locationCacheSize});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SettingsUIStateImplCopyWithImpl<$Res>
|
||||
extends _$SettingsUIStateCopyWithImpl<$Res, _$SettingsUIStateImpl>
|
||||
implements _$$SettingsUIStateImplCopyWith<$Res> {
|
||||
__$$SettingsUIStateImplCopyWithImpl(
|
||||
_$SettingsUIStateImpl _value, $Res Function(_$SettingsUIStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of SettingsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isEnableToolSiteMirrors = null,
|
||||
Object? inputGameLaunchECore = null,
|
||||
Object? customLauncherPath = freezed,
|
||||
Object? customGamePath = freezed,
|
||||
Object? locationCacheSize = null,
|
||||
}) {
|
||||
return _then(_$SettingsUIStateImpl(
|
||||
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
|
||||
? _value.isEnableToolSiteMirrors
|
||||
: isEnableToolSiteMirrors // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
inputGameLaunchECore: null == inputGameLaunchECore
|
||||
? _value.inputGameLaunchECore
|
||||
: inputGameLaunchECore // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
customLauncherPath: freezed == customLauncherPath
|
||||
? _value.customLauncherPath
|
||||
: customLauncherPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
customGamePath: freezed == customGamePath
|
||||
? _value.customGamePath
|
||||
: customGamePath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationCacheSize: null == locationCacheSize
|
||||
? _value.locationCacheSize
|
||||
: locationCacheSize // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$SettingsUIStateImpl implements _SettingsUIState {
|
||||
_$SettingsUIStateImpl(
|
||||
{this.isEnableToolSiteMirrors = false,
|
||||
this.inputGameLaunchECore = "0",
|
||||
this.customLauncherPath,
|
||||
this.customGamePath,
|
||||
this.locationCacheSize = 0});
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isEnableToolSiteMirrors;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String inputGameLaunchECore;
|
||||
@override
|
||||
final String? customLauncherPath;
|
||||
@override
|
||||
final String? customGamePath;
|
||||
@override
|
||||
@JsonKey()
|
||||
final int locationCacheSize;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SettingsUIState(isEnableToolSiteMirrors: $isEnableToolSiteMirrors, inputGameLaunchECore: $inputGameLaunchECore, customLauncherPath: $customLauncherPath, customGamePath: $customGamePath, locationCacheSize: $locationCacheSize)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SettingsUIStateImpl &&
|
||||
(identical(
|
||||
other.isEnableToolSiteMirrors, isEnableToolSiteMirrors) ||
|
||||
other.isEnableToolSiteMirrors == isEnableToolSiteMirrors) &&
|
||||
(identical(other.inputGameLaunchECore, inputGameLaunchECore) ||
|
||||
other.inputGameLaunchECore == inputGameLaunchECore) &&
|
||||
(identical(other.customLauncherPath, customLauncherPath) ||
|
||||
other.customLauncherPath == customLauncherPath) &&
|
||||
(identical(other.customGamePath, customGamePath) ||
|
||||
other.customGamePath == customGamePath) &&
|
||||
(identical(other.locationCacheSize, locationCacheSize) ||
|
||||
other.locationCacheSize == locationCacheSize));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
isEnableToolSiteMirrors,
|
||||
inputGameLaunchECore,
|
||||
customLauncherPath,
|
||||
customGamePath,
|
||||
locationCacheSize);
|
||||
|
||||
/// Create a copy of SettingsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SettingsUIStateImplCopyWith<_$SettingsUIStateImpl> get copyWith =>
|
||||
__$$SettingsUIStateImplCopyWithImpl<_$SettingsUIStateImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _SettingsUIState implements SettingsUIState {
|
||||
factory _SettingsUIState(
|
||||
{final bool isEnableToolSiteMirrors,
|
||||
final String inputGameLaunchECore,
|
||||
final String? customLauncherPath,
|
||||
final String? customGamePath,
|
||||
final int locationCacheSize}) = _$SettingsUIStateImpl;
|
||||
|
||||
@override
|
||||
bool get isEnableToolSiteMirrors;
|
||||
@override
|
||||
String get inputGameLaunchECore;
|
||||
@override
|
||||
String? get customLauncherPath;
|
||||
@override
|
||||
String? get customGamePath;
|
||||
@override
|
||||
int get locationCacheSize;
|
||||
|
||||
/// Create a copy of SettingsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SettingsUIStateImplCopyWith<_$SettingsUIStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'settings_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$settingsUIModelHash() => r'2be0fe230d23f84796e0a3a39dd3248c2aa90f23';
|
||||
|
||||
/// See also [SettingsUIModel].
|
||||
@ProviderFor(SettingsUIModel)
|
||||
final settingsUIModelProvider =
|
||||
AutoDisposeNotifierProvider<SettingsUIModel, SettingsUIState>.internal(
|
||||
SettingsUIModel.new,
|
||||
name: r'settingsUIModelProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$settingsUIModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$SettingsUIModel = AutoDisposeNotifier<SettingsUIState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,265 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/material.dart' show Material;
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:markdown/markdown.dart' as markdown;
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:html/parser.dart' as html_parser;
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class UpgradeDialogUI extends HookConsumerWidget {
|
||||
const UpgradeDialogUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final appState = ref.watch(appGlobalModelProvider);
|
||||
final appModel = ref.read(appGlobalModelProvider.notifier);
|
||||
final description = useState<String?>(null);
|
||||
final isUsingDiversion = useState(false);
|
||||
final isUpgrading = useState(false);
|
||||
final progress = useState(0.0);
|
||||
final downloadUrl = useState("");
|
||||
|
||||
final targetVersion = ConstConf.isMSE
|
||||
? appState.networkVersionData!.mSELastVersion!
|
||||
: appState.networkVersionData!.lastVersion!;
|
||||
|
||||
final minVersionCode = ConstConf.isMSE
|
||||
? appState.networkVersionData?.mSEMinVersionCode
|
||||
: appState.networkVersionData?.minVersionCode;
|
||||
|
||||
useEffect(() {
|
||||
_getUpdateInfo(context, targetVersion, description, downloadUrl);
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return Material(
|
||||
child: ContentDialog(
|
||||
title:
|
||||
Text(S.current.app_upgrade_title_new_version_found(targetVersion)),
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (description.value == null) ...[
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 16),
|
||||
Text(S.current
|
||||
.app_upgrade_info_getting_new_version_details)
|
||||
],
|
||||
),
|
||||
)
|
||||
] else
|
||||
...makeMarkdownView(description.value!,
|
||||
attachmentsUrl: URLConf.giteaAttachmentsUrl),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
if (isUsingDiversion.value) ...[
|
||||
const SizedBox(height: 24),
|
||||
GestureDetector(
|
||||
onTap: _launchReleaseUrl,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(.1),
|
||||
borderRadius: BorderRadius.circular(7)),
|
||||
child: Text(
|
||||
S.current.app_upgrade_info_update_server_tip,
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: Colors.white.withOpacity(.7)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (isUpgrading.value) ...[
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Text(progress.value == 100
|
||||
? S.current.app_upgrade_info_installing
|
||||
: S.current.app_upgrade_info_downloading(
|
||||
progress.value.toStringAsFixed(2))),
|
||||
Expanded(
|
||||
child: ProgressBar(
|
||||
value: progress.value == 100 ? null : progress.value,
|
||||
)),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
actions: isUpgrading.value
|
||||
? null
|
||||
: [
|
||||
if (downloadUrl.value.isNotEmpty)
|
||||
FilledButton(
|
||||
onPressed: () => _doUpgrade(
|
||||
context,
|
||||
appState,
|
||||
isUpgrading,
|
||||
appModel,
|
||||
downloadUrl,
|
||||
description,
|
||||
isUsingDiversion,
|
||||
progress),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4, bottom: 4, left: 8, right: 8),
|
||||
child: Text(S.current.app_upgrade_action_update_now),
|
||||
)),
|
||||
if (ConstConf.appVersionCode >= (minVersionCode ?? 0))
|
||||
Button(
|
||||
onPressed: () => _doCancel(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4, bottom: 4, left: 8, right: 8),
|
||||
child: Text(S.current.app_upgrade_action_next_time),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _getUpdateInfo(
|
||||
BuildContext context,
|
||||
String targetVersion,
|
||||
ValueNotifier<String?> description,
|
||||
ValueNotifier<String> downloadUrl) async {
|
||||
try {
|
||||
final r = await Api.getAppReleaseDataByVersionName(targetVersion);
|
||||
description.value = r["body"];
|
||||
final assets = List.of(r["assets"] ?? []);
|
||||
for (var asset in assets) {
|
||||
if (asset["name"].toString().endsWith("SETUP.exe")) {
|
||||
downloadUrl.value = asset["browser_download_url"];
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("UpgradeDialogUIModel.loadData Error : $e");
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context, false);
|
||||
}
|
||||
}
|
||||
|
||||
void _launchReleaseUrl() {
|
||||
launchUrlString(URLConf.devReleaseUrl);
|
||||
}
|
||||
|
||||
void _doCancel(BuildContext context) {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
|
||||
String _getDiversionUrl(String description) {
|
||||
try {
|
||||
final htmlStr = markdown.markdownToHtml(description);
|
||||
final html = html_parser.parse(htmlStr);
|
||||
for (var element in html.querySelectorAll('a')) {
|
||||
String linkText = element.text;
|
||||
String linkUrl = element.attributes['href'] ?? '';
|
||||
if (linkText.trim().endsWith("_SETUP.exe")) {
|
||||
final diversionDownloadUrl = linkUrl.trim();
|
||||
dPrint("diversionDownloadUrl === $diversionDownloadUrl");
|
||||
return diversionDownloadUrl;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("_checkDiversionUrl Error:$e");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Future<void> _doUpgrade(
|
||||
BuildContext context,
|
||||
AppGlobalState appState,
|
||||
ValueNotifier<bool> isUpgrading,
|
||||
AppGlobalModel appModel,
|
||||
ValueNotifier<String> downloadUrl,
|
||||
ValueNotifier<String?> description,
|
||||
ValueNotifier<bool> isUsingDiversion,
|
||||
ValueNotifier<double> progress) async {
|
||||
if (ConstConf.isMSE) {
|
||||
launchUrlString("ms-windows-store://pdp/?productid=9NF3SWFWNKL1");
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
if (ConstConf.appVersionCode <
|
||||
(appState.networkVersionData?.minVersionCode ?? 0)) {
|
||||
exit(0);
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
_doCancel(context);
|
||||
return;
|
||||
}
|
||||
isUpgrading.value = true;
|
||||
final fileName = "${appModel.getUpgradePath()}/next_SETUP.exe";
|
||||
try {
|
||||
// check diversionDownloadUrl
|
||||
var url = downloadUrl.value;
|
||||
final diversionDownloadUrl = _getDiversionUrl(description.value!);
|
||||
final dio = Dio();
|
||||
if (diversionDownloadUrl.isNotEmpty) {
|
||||
try {
|
||||
final resp = await dio.head(diversionDownloadUrl,
|
||||
options: Options(
|
||||
sendTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 10)));
|
||||
if (resp.statusCode == 200) {
|
||||
isUsingDiversion.value = true;
|
||||
url = diversionDownloadUrl;
|
||||
} else {
|
||||
isUsingDiversion.value = false;
|
||||
}
|
||||
dPrint("diversionDownloadUrl head resp == ${resp.headers}");
|
||||
} catch (e) {
|
||||
dPrint("diversionDownloadUrl err:$e");
|
||||
}
|
||||
}
|
||||
await dio.download(url, fileName,
|
||||
onReceiveProgress: (int count, int total) {
|
||||
progress.value = (count / total) * 100;
|
||||
});
|
||||
} catch (_) {
|
||||
isUpgrading.value = false;
|
||||
progress.value = 0;
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.app_upgrade_info_download_failed);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final r = await (Process.run(
|
||||
SystemHelper.powershellPath, ["start", fileName, "/SILENT"]));
|
||||
if (r.stderr.toString().isNotEmpty) {
|
||||
throw r.stderr;
|
||||
}
|
||||
exit(0);
|
||||
} catch (_) {
|
||||
isUpgrading.value = false;
|
||||
progress.value = 0;
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.app_upgrade_info_run_failed);
|
||||
SystemHelper.openDir(fileName);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_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';
|
||||
|
||||
class SplashUI extends HookConsumerWidget {
|
||||
@ -86,17 +85,18 @@ class SplashUI extends HookConsumerWidget {
|
||||
dPrint("_initApp checkUpdate");
|
||||
await appModel.checkUpdate(context);
|
||||
stepState.value = 2;
|
||||
dPrint("_initApp aria2cModelProvider");
|
||||
ref.read(aria2cModelProvider);
|
||||
if (!context.mounted) return;
|
||||
context.go("/index");
|
||||
context.go("/");
|
||||
}
|
||||
|
||||
_showAlert(BuildContext context, Box<dynamic> 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),
|
||||
MarkdownWidget(
|
||||
data: S.current.app_splash_dialog_u_a_p_p_content,
|
||||
shrinkWrap: true,
|
||||
),
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .5));
|
||||
if (userOk) {
|
||||
|
@ -1,296 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/async.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class HostsBoosterDialogUI extends HookConsumerWidget {
|
||||
const HostsBoosterDialogUI({super.key});
|
||||
|
||||
static final _hostsMap = {
|
||||
"Recaptcha": ["www.recaptcha.net", "recaptcha.net"],
|
||||
S.current.tools_hosts_info_rsi_official_website: [
|
||||
"robertsspaceindustries.com"
|
||||
],
|
||||
S.current.tools_hosts_info_rsi_customer_service: [
|
||||
"support.robertsspaceindustries.com"
|
||||
],
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final checkedMap = useState<Map<String, bool>>({});
|
||||
final workingMap = useState<Map<String, int?>>({});
|
||||
final workingText = useState<String>("");
|
||||
|
||||
doHost(BuildContext context) async {
|
||||
if (workingMap.value.isEmpty) {
|
||||
final hasTrue =
|
||||
checkedMap.value.values.where((element) => element).firstOrNull !=
|
||||
null;
|
||||
if (!hasTrue) {
|
||||
for (var k in _hostsMap.keys) {
|
||||
checkedMap.value[k] = true;
|
||||
}
|
||||
checkedMap.value = Map.from(checkedMap.value);
|
||||
}
|
||||
}
|
||||
workingText.value = S.current.tools_hosts_info_dns_query_and_test;
|
||||
final ipsMap = await _doCheckDns(workingMap, checkedMap);
|
||||
workingText.value = S.current.tools_hosts_info_writing_hosts;
|
||||
if (!context.mounted) return;
|
||||
await _doWriteHosts(ipsMap).unwrap(context: context);
|
||||
workingText.value = S.current.tools_hosts_info_reading_config;
|
||||
await _readHostsState(workingMap, checkedMap);
|
||||
workingText.value = "";
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
AnalyticsApi.touch("host_dns_boost");
|
||||
// 监听 Hosts 文件变更
|
||||
_readHostsState(workingMap, checkedMap);
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return ContentDialog(
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55),
|
||||
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_hosts_info_hosts_acceleration),
|
||||
const Spacer(),
|
||||
Button(
|
||||
onPressed: () => _openHostsFile(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.open_file),
|
||||
const SizedBox(width: 6),
|
||||
Text(S.current.tools_hosts_info_open_hosts_file),
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
content: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.tools_hosts_info_status),
|
||||
const SizedBox(width: 38),
|
||||
Text(S.current.tools_hosts_info_site),
|
||||
const Spacer(),
|
||||
Text(S.current.tools_hosts_info_enable),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ListView.builder(
|
||||
itemCount: _hostsMap.length,
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(6),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final isEnable =
|
||||
checkedMap.value[_hostsMap.keys.elementAt(index)] ?? false;
|
||||
final workingState =
|
||||
workingMap.value[_hostsMap.keys.elementAt(index)];
|
||||
return 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: [
|
||||
if (workingState == null)
|
||||
Icon(FontAwesomeIcons.xmark,
|
||||
size: 24, color: Colors.red),
|
||||
if (workingState == 0)
|
||||
const SizedBox(
|
||||
width: 24, height: 24, child: ProgressRing()),
|
||||
if (workingState == 1)
|
||||
Icon(FontAwesomeIcons.check,
|
||||
size: 24, color: Colors.green),
|
||||
const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
Text(_hostsMap.keys.elementAt(index)),
|
||||
const Spacer(),
|
||||
ToggleSwitch(
|
||||
onChanged: (value) {
|
||||
checkedMap.value[_hostsMap.keys.elementAt(index)] =
|
||||
value;
|
||||
checkedMap.value = Map.from(checkedMap.value);
|
||||
},
|
||||
checked: isEnable,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (workingText.value.isNotEmpty)
|
||||
SizedBox(
|
||||
height: 86,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 42, width: 42, child: ProgressRing()),
|
||||
const SizedBox(height: 12),
|
||||
Text(workingText.value),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: FilledButton(
|
||||
onPressed: () => doHost(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 3, bottom: 3, left: 12, right: 12),
|
||||
child: Text(
|
||||
S.current.tools_hosts_action_one_click_acceleration),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openHostsFile(BuildContext context) async {
|
||||
// 使用管理员权限调用记事本${S.current.tools_hosts_info_open_hosts_file}
|
||||
Process.run(SystemHelper.powershellPath, [
|
||||
"-Command",
|
||||
"Start-Process notepad.exe -Verb runAs -ArgumentList ${SystemHelper.getHostsFilePath()}"
|
||||
// ignore: use_build_context_synchronously
|
||||
]).unwrap(context: context);
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _doCheckDns(
|
||||
ValueNotifier<Map<String, int?>> workingMap,
|
||||
ValueNotifier<Map<String, bool>> checkedMap) async {
|
||||
Map<String, String> result = {};
|
||||
final trueLen = checkedMap.value.values.where((element) => element).length;
|
||||
if (trueLen == 0) {
|
||||
return result;
|
||||
}
|
||||
for (var kv in _hostsMap.entries) {
|
||||
final siteName = kv.key;
|
||||
final siteHost = kv.value.first;
|
||||
if (!(checkedMap.value[siteName] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
workingMap.value[siteName] = 0;
|
||||
workingMap.value = Map.from(workingMap.value);
|
||||
RSHttp.dnsLookupIps(siteHost).then((ips) async {
|
||||
int tryCount = ips.length;
|
||||
try {
|
||||
for (var ip in ips) {
|
||||
final resp =
|
||||
await RSHttp.head("https://$siteHost", withIpAddress: ip);
|
||||
dPrint(
|
||||
"[HostsBooster] host== $siteHost ip== $ip resp== ${resp.headers}");
|
||||
if (resp.headers.isNotEmpty) {
|
||||
if (result[siteName] == null) {
|
||||
result[siteName] = ip;
|
||||
workingMap.value[siteName] = 1;
|
||||
workingMap.value = Map.from(workingMap.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
tryCount--;
|
||||
if (tryCount == 0) {
|
||||
workingMap.value[siteName] = null;
|
||||
workingMap.value = Map.from(workingMap.value);
|
||||
result[siteName] = "";
|
||||
}
|
||||
}
|
||||
}, onError: (e) {
|
||||
workingMap.value[siteName] = null;
|
||||
workingMap.value = Map.from(workingMap.value);
|
||||
result[siteName] = "";
|
||||
});
|
||||
}
|
||||
while (true) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (result.length == trueLen) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _doWriteHosts(Map<String, String> ipsMap) async {
|
||||
// 读取 hosts 文件
|
||||
final hostsFile = File(SystemHelper.getHostsFilePath());
|
||||
final hostsFileString = await hostsFile.readAsString();
|
||||
final hostsFileLines = hostsFileString.split("\n");
|
||||
final newHostsFileLines = <String>[];
|
||||
|
||||
// copy Lines
|
||||
for (var line in hostsFileLines) {
|
||||
if (line.contains("#StarCitizenToolBox")) {
|
||||
break;
|
||||
}
|
||||
newHostsFileLines.add(line);
|
||||
}
|
||||
dPrint("userHostsFile == $hostsFileString");
|
||||
for (var kv in ipsMap.entries) {
|
||||
final domains = _hostsMap[kv.key] ?? <String>[];
|
||||
for (var domain in domains) {
|
||||
if (kv.value != "") {
|
||||
newHostsFileLines
|
||||
.add("${kv.value} $domain #StarCitizenToolBox");
|
||||
}
|
||||
}
|
||||
}
|
||||
await hostsFile.writeAsString(newHostsFileLines.join("\n"), flush: true);
|
||||
}
|
||||
|
||||
Future<void> _readHostsState(ValueNotifier<Map<String, int?>> workingMap,
|
||||
ValueNotifier<Map<String, bool>> checkedMap) async {
|
||||
workingMap.value.clear();
|
||||
final hostsFile = File(SystemHelper.getHostsFilePath());
|
||||
final hostsFileString = await hostsFile.readAsString();
|
||||
final hostsFileLines = hostsFileString.split("\n");
|
||||
dPrint("userHostsFile == $hostsFileString");
|
||||
for (var line in hostsFileLines) {
|
||||
if (line.contains("#StarCitizenToolBox")) {
|
||||
for (var host in _hostsMap.entries) {
|
||||
if (line.contains(" ${host.value.first}")) {
|
||||
workingMap.value[host.key] = 1;
|
||||
workingMap.value = Map.from(workingMap.value);
|
||||
checkedMap.value[host.key] = true;
|
||||
checkedMap.value = Map.from(checkedMap.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,482 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
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';
|
||||
|
||||
part 'rsi_launcher_enhance_dialog_ui.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class RSILauncherStateData with _$RSILauncherStateData {
|
||||
const factory RSILauncherStateData({
|
||||
required String version,
|
||||
required asar_api.RsiLauncherAsarData data,
|
||||
required String serverData,
|
||||
@Default(false) bool isPatchInstalled,
|
||||
String? enabledLocalization,
|
||||
bool? enableDownloaderBoost,
|
||||
}) = _RSILauncherStateData;
|
||||
}
|
||||
|
||||
class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
final bool showNotGameInstallMsg;
|
||||
|
||||
const RsiLauncherEnhanceDialogUI(
|
||||
{super.key, this.showNotGameInstallMsg = false});
|
||||
|
||||
static const supportLocalizationMap = {
|
||||
"en": NoL10n.langEn,
|
||||
"zh_CN": NoL10n.langZHS,
|
||||
"zh_TW": NoL10n.langZHT,
|
||||
"fr": NoL10n.langFR,
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final workingText = useState("");
|
||||
|
||||
final assarState = useState<RSILauncherStateData?>(null);
|
||||
|
||||
final expandEnhance = useState(false);
|
||||
|
||||
Future<void> readState() async {
|
||||
workingText.value = S.current.tools_rsi_launcher_enhance_init_msg1;
|
||||
assarState.value = await _readState(context).unwrap(context: context);
|
||||
if (assarState.value == null) {
|
||||
workingText.value = "";
|
||||
return;
|
||||
}
|
||||
workingText.value = S.current.tools_rsi_launcher_enhance_init_msg2;
|
||||
if (!context.mounted) return;
|
||||
await _loadEnhanceData(context, ref, assarState)
|
||||
.unwrap(context: context)
|
||||
.unwrap(context: context);
|
||||
workingText.value = "";
|
||||
}
|
||||
|
||||
void doInstall() 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));
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
workingText.value = S.current.tools_rsi_launcher_enhance_working_msg1;
|
||||
final newScript =
|
||||
await _genNewScript(assarState).unwrap(context: context);
|
||||
workingText.value = S.current.tools_rsi_launcher_enhance_working_msg2;
|
||||
if (!context.mounted) return;
|
||||
await assarState.value?.data
|
||||
.writeMainJs(content: utf8.encode(newScript))
|
||||
.unwrap(context: context);
|
||||
AnalyticsApi.touch("rsi_launcher_mod_apply");
|
||||
await readState();
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
AnalyticsApi.touch("rsi_launcher_mod_launch");
|
||||
readState();
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
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),
|
||||
]),
|
||||
content: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 130),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (showNotGameInstallMsg) ...[
|
||||
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,
|
||||
),
|
||||
],
|
||||
if (workingText.value.isNotEmpty) ...[
|
||||
Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Row(),
|
||||
const SizedBox(height: 12),
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(workingText.value),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
S.current.tools_rsi_launcher_enhance_msg_version(
|
||||
assarState.value?.version ?? ""),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.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.withOpacity(.6),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (assarState.value?.serverData.isEmpty ?? true) ...[
|
||||
Text(S.current.tools_rsi_launcher_enhance_msg_error),
|
||||
] else ...[
|
||||
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.withOpacity(.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(
|
||||
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.withOpacity(.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),
|
||||
))),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_msg_uninstall,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.6), fontSize: 13),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<RSILauncherStateData?> _readState(BuildContext context) async {
|
||||
final lPath = await SystemHelper.getRSILauncherPath(skipEXE: true);
|
||||
if (lPath.isEmpty) {
|
||||
if (!context.mounted) return null;
|
||||
showToast(context,
|
||||
S.current.tools_rsi_launcher_enhance_msg_error_launcher_notfound);
|
||||
return null;
|
||||
}
|
||||
dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath ==== $lPath");
|
||||
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 mainJsString = String.fromCharCodes(data.mainJsContent);
|
||||
|
||||
final (enabledLocalization, enableDownloaderBoost) =
|
||||
_readScriptState(mainJsString);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _loadEnhanceData(BuildContext context, WidgetRef ref,
|
||||
ValueNotifier<RSILauncherStateData?> assarState) async {
|
||||
final globalModel = ref.read(appGlobalModelProvider);
|
||||
final enhancePath =
|
||||
"${globalModel.applicationSupportDir}/launcher_enhance_data";
|
||||
final enhanceFile =
|
||||
File("$enhancePath/${assarState.value?.version}.tar.gz");
|
||||
if (!await enhanceFile.exists()) {
|
||||
final downloadUrl =
|
||||
"${URLConf.gitApiRSILauncherEnhanceUrl}/archive/${assarState.value?.version}.tar.gz";
|
||||
final r = await RSHttp.get(downloadUrl).unwrap();
|
||||
if (r.statusCode != 200 || r.data == null) {
|
||||
return "";
|
||||
}
|
||||
await enhanceFile.create(recursive: true);
|
||||
await enhanceFile.writeAsBytes(r.data!, flush: true);
|
||||
}
|
||||
final severMainJS =
|
||||
await compute(_readArchive, (enhanceFile.path, "main.js"));
|
||||
final serverMainJSString = severMainJS.toString();
|
||||
final scriptState = _readScriptState(serverMainJSString);
|
||||
if (assarState.value?.enabledLocalization == null) {
|
||||
assarState.value =
|
||||
assarState.value?.copyWith(enabledLocalization: scriptState.$1);
|
||||
dPrint(
|
||||
"[RsiLauncherEnhanceDialogUI] _loadEnhanceData enabledLocalization == ${scriptState.$1}");
|
||||
}
|
||||
if (assarState.value?.enableDownloaderBoost == null) {
|
||||
assarState.value =
|
||||
assarState.value?.copyWith(enableDownloaderBoost: scriptState.$2);
|
||||
dPrint(
|
||||
"[RsiLauncherEnhanceDialogUI] _loadEnhanceData enableDownloaderBoost == ${scriptState.$2}");
|
||||
}
|
||||
assarState.value =
|
||||
assarState.value?.copyWith(serverData: serverMainJSString);
|
||||
return serverMainJSString;
|
||||
}
|
||||
|
||||
static StringBuffer _readArchive((String savePath, String fileName) data) {
|
||||
final inputStream = InputFileStream(data.$1);
|
||||
final archive =
|
||||
TarDecoder().decodeBytes(GZipDecoder().decodeBuffer(inputStream));
|
||||
StringBuffer dataBuffer = StringBuffer("");
|
||||
for (var element in archive.files) {
|
||||
if (element.name.endsWith(data.$2)) {
|
||||
for (var value
|
||||
in (element.rawContent?.readString() ?? "").split("\n")) {
|
||||
final tv = value;
|
||||
if (tv.isNotEmpty) dataBuffer.writeln(tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
archive.clear();
|
||||
return dataBuffer;
|
||||
}
|
||||
|
||||
// ignore: constant_identifier_names
|
||||
static const SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START =
|
||||
"const SC_TOOLBOX_ENABLED_LOCALIZATION = ";
|
||||
|
||||
// ignore: constant_identifier_names
|
||||
static const SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START =
|
||||
"const SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST = ";
|
||||
|
||||
(String?, bool?) _readScriptState(String mainJsString) {
|
||||
String? enabledLocalization;
|
||||
bool? enableDownloaderBoost;
|
||||
for (final line in mainJsString.split("\n")) {
|
||||
final lineTrim = line.trim();
|
||||
if (lineTrim.startsWith(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START)) {
|
||||
enabledLocalization = lineTrim
|
||||
.substring(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START.length)
|
||||
.replaceAll("\"", "")
|
||||
.replaceAll(";", "");
|
||||
} else if (lineTrim
|
||||
.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) {
|
||||
enableDownloaderBoost = lineTrim
|
||||
.substring(
|
||||
SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START.length)
|
||||
.toLowerCase() ==
|
||||
"true;";
|
||||
}
|
||||
}
|
||||
return (enabledLocalization, enableDownloaderBoost);
|
||||
}
|
||||
|
||||
Future<String> _genNewScript(
|
||||
ValueNotifier<RSILauncherStateData?> assarState) async {
|
||||
final serverScriptLines = assarState.value!.serverData.split("\n");
|
||||
final StringBuffer scriptBuffer = StringBuffer("");
|
||||
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}\";");
|
||||
} else if (lineTrim
|
||||
.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) {
|
||||
scriptBuffer.writeln(
|
||||
"$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};");
|
||||
} else {
|
||||
scriptBuffer.writeln(line);
|
||||
}
|
||||
}
|
||||
return scriptBuffer.toString();
|
||||
}
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'rsi_launcher_enhance_dialog_ui.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$RSILauncherStateData {
|
||||
String get version => throw _privateConstructorUsedError;
|
||||
asar_api.RsiLauncherAsarData get data => throw _privateConstructorUsedError;
|
||||
String get serverData => throw _privateConstructorUsedError;
|
||||
bool get isPatchInstalled => throw _privateConstructorUsedError;
|
||||
String? get enabledLocalization => throw _privateConstructorUsedError;
|
||||
bool? get enableDownloaderBoost => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of RSILauncherStateData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$RSILauncherStateDataCopyWith<RSILauncherStateData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $RSILauncherStateDataCopyWith<$Res> {
|
||||
factory $RSILauncherStateDataCopyWith(RSILauncherStateData value,
|
||||
$Res Function(RSILauncherStateData) then) =
|
||||
_$RSILauncherStateDataCopyWithImpl<$Res, RSILauncherStateData>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String version,
|
||||
asar_api.RsiLauncherAsarData data,
|
||||
String serverData,
|
||||
bool isPatchInstalled,
|
||||
String? enabledLocalization,
|
||||
bool? enableDownloaderBoost});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$RSILauncherStateDataCopyWithImpl<$Res,
|
||||
$Val extends RSILauncherStateData>
|
||||
implements $RSILauncherStateDataCopyWith<$Res> {
|
||||
_$RSILauncherStateDataCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// 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,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
version: null == version
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
data: null == data
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as asar_api.RsiLauncherAsarData,
|
||||
serverData: null == serverData
|
||||
? _value.serverData
|
||||
: serverData // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isPatchInstalled: null == isPatchInstalled
|
||||
? _value.isPatchInstalled
|
||||
: isPatchInstalled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
enabledLocalization: freezed == enabledLocalization
|
||||
? _value.enabledLocalization
|
||||
: enabledLocalization // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
enableDownloaderBoost: freezed == enableDownloaderBoost
|
||||
? _value.enableDownloaderBoost
|
||||
: enableDownloaderBoost // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$RSILauncherStateDataImplCopyWith<$Res>
|
||||
implements $RSILauncherStateDataCopyWith<$Res> {
|
||||
factory _$$RSILauncherStateDataImplCopyWith(_$RSILauncherStateDataImpl value,
|
||||
$Res Function(_$RSILauncherStateDataImpl) then) =
|
||||
__$$RSILauncherStateDataImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String version,
|
||||
asar_api.RsiLauncherAsarData data,
|
||||
String serverData,
|
||||
bool isPatchInstalled,
|
||||
String? enabledLocalization,
|
||||
bool? enableDownloaderBoost});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$RSILauncherStateDataImplCopyWithImpl<$Res>
|
||||
extends _$RSILauncherStateDataCopyWithImpl<$Res, _$RSILauncherStateDataImpl>
|
||||
implements _$$RSILauncherStateDataImplCopyWith<$Res> {
|
||||
__$$RSILauncherStateDataImplCopyWithImpl(_$RSILauncherStateDataImpl _value,
|
||||
$Res Function(_$RSILauncherStateDataImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// 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,
|
||||
}) {
|
||||
return _then(_$RSILauncherStateDataImpl(
|
||||
version: null == version
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
data: null == data
|
||||
? _value.data
|
||||
: data // ignore: cast_nullable_to_non_nullable
|
||||
as asar_api.RsiLauncherAsarData,
|
||||
serverData: null == serverData
|
||||
? _value.serverData
|
||||
: serverData // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isPatchInstalled: null == isPatchInstalled
|
||||
? _value.isPatchInstalled
|
||||
: isPatchInstalled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
enabledLocalization: freezed == enabledLocalization
|
||||
? _value.enabledLocalization
|
||||
: enabledLocalization // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
enableDownloaderBoost: freezed == enableDownloaderBoost
|
||||
? _value.enableDownloaderBoost
|
||||
: enableDownloaderBoost // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$RSILauncherStateDataImpl implements _RSILauncherStateData {
|
||||
const _$RSILauncherStateDataImpl(
|
||||
{required this.version,
|
||||
required this.data,
|
||||
required this.serverData,
|
||||
this.isPatchInstalled = false,
|
||||
this.enabledLocalization,
|
||||
this.enableDownloaderBoost});
|
||||
|
||||
@override
|
||||
final String version;
|
||||
@override
|
||||
final asar_api.RsiLauncherAsarData data;
|
||||
@override
|
||||
final String serverData;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isPatchInstalled;
|
||||
@override
|
||||
final String? enabledLocalization;
|
||||
@override
|
||||
final bool? enableDownloaderBoost;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RSILauncherStateData(version: $version, data: $data, serverData: $serverData, isPatchInstalled: $isPatchInstalled, enabledLocalization: $enabledLocalization, enableDownloaderBoost: $enableDownloaderBoost)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$RSILauncherStateDataImpl &&
|
||||
(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));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, version, data, serverData,
|
||||
isPatchInstalled, enabledLocalization, enableDownloaderBoost);
|
||||
|
||||
/// Create a copy of RSILauncherStateData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$RSILauncherStateDataImplCopyWith<_$RSILauncherStateDataImpl>
|
||||
get copyWith =>
|
||||
__$$RSILauncherStateDataImplCopyWithImpl<_$RSILauncherStateDataImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _RSILauncherStateData implements RSILauncherStateData {
|
||||
const factory _RSILauncherStateData(
|
||||
{required final String version,
|
||||
required final asar_api.RsiLauncherAsarData data,
|
||||
required final String serverData,
|
||||
final bool isPatchInstalled,
|
||||
final String? enabledLocalization,
|
||||
final bool? enableDownloaderBoost}) = _$RSILauncherStateDataImpl;
|
||||
|
||||
@override
|
||||
String get version;
|
||||
@override
|
||||
asar_api.RsiLauncherAsarData get data;
|
||||
@override
|
||||
String get serverData;
|
||||
@override
|
||||
bool get isPatchInstalled;
|
||||
@override
|
||||
String? get enabledLocalization;
|
||||
@override
|
||||
bool? get enableDownloaderBoost;
|
||||
|
||||
/// Create a copy of RSILauncherStateData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$RSILauncherStateDataImplCopyWith<_$RSILauncherStateDataImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
@ -1,265 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
class ToolsUI extends HookConsumerWidget {
|
||||
const ToolsUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(toolsUIModelProvider);
|
||||
final model = ref.read(toolsUIModelProvider.notifier);
|
||||
|
||||
useEffect(() {
|
||||
addPostFrameCallback(() {
|
||||
model.loadToolsCard(context, skipPathScan: false);
|
||||
});
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
makeGameLauncherPathSelect(context, model, state),
|
||||
const SizedBox(height: 12),
|
||||
makeGamePathSelect(context, model, state),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: state.working
|
||||
? null
|
||||
: () =>
|
||||
model.loadToolsCard(context, skipPathScan: false),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 30, bottom: 30, left: 12, right: 12),
|
||||
child: Icon(FluentIcons.refresh),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (state.items.isEmpty)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.tools_info_scanning),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: MasonryGridView.count(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
itemCount: (state.isItemLoading)
|
||||
? state.items.length + 1
|
||||
: state.items.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == state.items.length) {
|
||||
return Container(
|
||||
width: 300,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
),
|
||||
child: makeLoading(context));
|
||||
}
|
||||
final item = state.items[index];
|
||||
return Container(
|
||||
width: 300,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(.2),
|
||||
borderRadius:
|
||||
BorderRadius.circular(1000)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: item.icon,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.name,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
)),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
item.infoString,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white.withOpacity(.6)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
Button(
|
||||
onPressed: state.working
|
||||
? null
|
||||
: item.onTap == null
|
||||
? null
|
||||
: () {
|
||||
try {
|
||||
item.onTap?.call();
|
||||
} catch (e) {
|
||||
showToast(
|
||||
context,
|
||||
S.current
|
||||
.tools_info_processing_failed(
|
||||
e));
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.play),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (state.working)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(150),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.doctor_info_processing),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeGamePathSelect(
|
||||
BuildContext context, ToolsUIModel model, ToolsUIState state) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current.tools_info_game_install_location),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: ComboBox<String>(
|
||||
isExpanded: true,
|
||||
value: state.scInstalledPath,
|
||||
items: [
|
||||
for (final path in state.scInstallPaths)
|
||||
ComboBoxItem(
|
||||
value: path,
|
||||
child: Text(path),
|
||||
)
|
||||
],
|
||||
onChanged: (v) {
|
||||
model.loadToolsCard(context, skipPathScan: true);
|
||||
model.onChangeGamePath(v!);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.folder_open),
|
||||
),
|
||||
onPressed: () => model.openDir(state.scInstalledPath))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeGameLauncherPathSelect(
|
||||
BuildContext context, ToolsUIModel model, ToolsUIState state) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current.tools_info_rsi_launcher_location),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: ComboBox<String>(
|
||||
isExpanded: true,
|
||||
value: state.rsiLauncherInstalledPath,
|
||||
items: [
|
||||
for (final path in state.rsiLauncherInstallPaths)
|
||||
ComboBoxItem(
|
||||
value: path,
|
||||
child: Text(path),
|
||||
)
|
||||
],
|
||||
onChanged: (v) {
|
||||
model.loadToolsCard(context, skipPathScan: true);
|
||||
model.onChangeLauncherPath(v!);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.folder_open),
|
||||
),
|
||||
onPressed: () => model.openDir(state.rsiLauncherInstalledPath))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,613 +0,0 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:go_router/go_router.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/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.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/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/downloader/home_downloader_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
|
||||
import 'dialogs/hosts_booster_dialog_ui.dart';
|
||||
import 'dialogs/rsi_launcher_enhance_dialog_ui.dart';
|
||||
|
||||
part 'tools_ui_model.g.dart';
|
||||
|
||||
part 'tools_ui_model.freezed.dart';
|
||||
|
||||
class ToolsItemData {
|
||||
String key;
|
||||
|
||||
ToolsItemData(this.key, this.name, this.infoString, this.icon, {this.onTap});
|
||||
|
||||
String name;
|
||||
String infoString;
|
||||
Widget icon;
|
||||
AsyncCallback? onTap;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ToolsUIState with _$ToolsUIState {
|
||||
factory ToolsUIState({
|
||||
@Default(false) bool working,
|
||||
@Default("") String scInstalledPath,
|
||||
@Default("") String rsiLauncherInstalledPath,
|
||||
@Default([]) List<String> scInstallPaths,
|
||||
@Default([]) List<String> rsiLauncherInstallPaths,
|
||||
@Default([]) List<ToolsItemData> items,
|
||||
@Default(false) bool isItemLoading,
|
||||
}) = _ToolsUIState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ToolsUIModel extends _$ToolsUIModel {
|
||||
@override
|
||||
ToolsUIState build() {
|
||||
state = ToolsUIState();
|
||||
return state;
|
||||
}
|
||||
|
||||
loadToolsCard(BuildContext context, {bool skipPathScan = false}) async {
|
||||
if (state.isItemLoading) return;
|
||||
var items = <ToolsItemData>[];
|
||||
state = state.copyWith(items: items, isItemLoading: true);
|
||||
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(
|
||||
"p4k_downloader",
|
||||
S.current.tools_action_p4k_download_repair,
|
||||
S.current.tools_action_info_p4k_download_repair_tip,
|
||||
const Icon(FontAwesomeIcons.download, size: 24),
|
||||
onTap: () => _downloadP4k(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(
|
||||
"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);
|
||||
if (!context.mounted) return;
|
||||
items.add(await _addPhotographyCard(context));
|
||||
state = state.copyWith(items: items);
|
||||
if (!context.mounted) return;
|
||||
items.addAll(await _addLogCard(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;
|
||||
showToast(context, S.current.tools_action_info_init_failed(e));
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ToolsItemData>> _addLogCard(BuildContext context) async {
|
||||
double logPathLen = 0;
|
||||
try {
|
||||
logPathLen =
|
||||
(await File(await SCLoggerHelper.getLogFilePath() ?? "").length()) /
|
||||
1024 /
|
||||
1024;
|
||||
} catch (_) {}
|
||||
return [
|
||||
ToolsItemData(
|
||||
"rsilauncher_log_fix",
|
||||
S.current.tools_action_rsi_launcher_log_fix,
|
||||
S.current.tools_action_info_rsi_launcher_log_issue(
|
||||
logPathLen.toStringAsFixed(4)),
|
||||
const Icon(FontAwesomeIcons.bookBible, size: 24),
|
||||
onTap: () => _rsiLogFix(context),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<List<ToolsItemData>> _addNvmePatchCard(BuildContext context) async {
|
||||
final nvmePatchStatus = await SystemHelper.checkNvmePatchStatus();
|
||||
return [
|
||||
if (nvmePatchStatus)
|
||||
ToolsItemData(
|
||||
"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),
|
||||
const Icon(FluentIcons.hard_drive, size: 24),
|
||||
onTap: nvmePatchStatus
|
||||
? () async {
|
||||
state = state.copyWith(working: true);
|
||||
await SystemHelper.doRemoveNvmePath();
|
||||
state = state.copyWith(working: false);
|
||||
if (!context.mounted) return;
|
||||
showToast(context,
|
||||
S.current.tools_action_info_removed_restart_effective);
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
if (!nvmePatchStatus)
|
||||
ToolsItemData(
|
||||
"add_nvme_settings",
|
||||
S.current.tools_action_write_nvme_registry_patch,
|
||||
S.current.tools_action_info_manual_nvme_patch,
|
||||
const Icon(FontAwesomeIcons.cashRegister, size: 24),
|
||||
onTap: () async {
|
||||
state = state.copyWith(working: true);
|
||||
final r = await SystemHelper.addNvmePatch();
|
||||
if (r == "") {
|
||||
if (!context.mounted) return;
|
||||
showToast(
|
||||
context, S.current.tools_action_info_fix_success_restart);
|
||||
} else {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.doctor_action_result_fix_fail(r));
|
||||
}
|
||||
state = state.copyWith(working: false);
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
},
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
Future<ToolsItemData> _addShaderCard(BuildContext context) async {
|
||||
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
final shaderSize = ((await SystemHelper.getDirLen(gameShaderCachePath ?? "",
|
||||
skipPath: ["$gameShaderCachePath\\Crashes"])) /
|
||||
1024 /
|
||||
1024)
|
||||
.toStringAsFixed(4);
|
||||
return ToolsItemData(
|
||||
"clean_shaders",
|
||||
S.current.tools_action_clear_shader_cache,
|
||||
S.current.tools_action_info_shader_cache_issue(shaderSize),
|
||||
const Icon(FontAwesomeIcons.shapes, size: 24),
|
||||
onTap: () => _cleanShaderCache(context),
|
||||
);
|
||||
}
|
||||
|
||||
Future<ToolsItemData> _addPhotographyCard(BuildContext context) async {
|
||||
// 获取配置文件状态
|
||||
final isEnable = await _checkPhotographyStatus(context);
|
||||
|
||||
return ToolsItemData(
|
||||
"photography_mode",
|
||||
isEnable
|
||||
? S.current.tools_action_close_photography_mode
|
||||
: S.current.tools_action_open_photography_mode,
|
||||
isEnable
|
||||
? S.current.tools_action_info_restore_lens_shake
|
||||
: S.current.tools_action_info_one_key_close_lens_shake,
|
||||
const Icon(FontAwesomeIcons.camera, size: 24),
|
||||
onTap: () => _onChangePhotographyMode(context, isEnable),
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------------------- func -------------------------------------------------------
|
||||
/// -----------------------------------------------------------------------------------------
|
||||
/// -----------------------------------------------------------------------------------------
|
||||
/// -----------------------------------------------------------------------------------------
|
||||
|
||||
Future<void> reScanPath(BuildContext context) async {
|
||||
var scInstallPaths = <String>[];
|
||||
var rsiLauncherInstallPaths = <String>[];
|
||||
var scInstalledPath = "";
|
||||
var rsiLauncherInstalledPath = "";
|
||||
|
||||
state = state.copyWith(
|
||||
scInstalledPath: scInstalledPath,
|
||||
rsiLauncherInstalledPath: rsiLauncherInstalledPath,
|
||||
scInstallPaths: scInstallPaths,
|
||||
rsiLauncherInstallPaths: rsiLauncherInstallPaths,
|
||||
);
|
||||
|
||||
try {
|
||||
rsiLauncherInstalledPath = await SystemHelper.getRSILauncherPath();
|
||||
rsiLauncherInstallPaths.add(rsiLauncherInstalledPath);
|
||||
final listData = await SCLoggerHelper.getLauncherLogList();
|
||||
if (listData == null) {
|
||||
return;
|
||||
}
|
||||
scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
|
||||
checkExists: false, withVersion: ["LIVE", "PTU", "EPTU"]);
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
scInstalledPath = scInstallPaths.first;
|
||||
}
|
||||
state = state.copyWith(
|
||||
scInstalledPath: scInstalledPath,
|
||||
rsiLauncherInstalledPath: rsiLauncherInstalledPath,
|
||||
scInstallPaths: scInstallPaths,
|
||||
rsiLauncherInstallPaths: rsiLauncherInstallPaths,
|
||||
);
|
||||
} catch (e) {
|
||||
dPrint(e);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_log_file_parse_failed);
|
||||
}
|
||||
|
||||
if (rsiLauncherInstalledPath == "") {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_rsi_launcher_not_found);
|
||||
}
|
||||
if (scInstalledPath == "") {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_star_citizen_not_found);
|
||||
}
|
||||
}
|
||||
|
||||
/// 重装EAC
|
||||
Future<void> _reinstallEAC(BuildContext context) async {
|
||||
if (state.scInstalledPath.isEmpty) {
|
||||
showToast(
|
||||
context, S.current.tools_action_info_valid_game_directory_needed);
|
||||
return;
|
||||
}
|
||||
state = state.copyWith(working: true);
|
||||
try {
|
||||
final eacPath = "${state.scInstalledPath}\\EasyAntiCheat";
|
||||
final eacJsonPath = "$eacPath\\Settings.json";
|
||||
if (await File(eacJsonPath).exists()) {
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final eacJsonData = await File(eacJsonPath).readAsString();
|
||||
final Map eacJson = json.decode(eacJsonData);
|
||||
final eacID = eacJson["productid"];
|
||||
if (eacID != null) {
|
||||
final eacCacheDir =
|
||||
Directory("${envVars["appdata"]}\\EasyAntiCheat\\$eacID");
|
||||
if (await eacCacheDir.exists()) {
|
||||
await eacCacheDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
final dir = Directory(eacPath);
|
||||
if (await dir.exists()) {
|
||||
await dir.delete(recursive: true);
|
||||
}
|
||||
final eacLauncher =
|
||||
File("${state.scInstalledPath}\\StarCitizen_Launcher.exe");
|
||||
if (await eacLauncher.exists()) {
|
||||
await eacLauncher.delete(recursive: true);
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_eac_file_removed);
|
||||
_adminRSILauncher(context);
|
||||
} catch (e) {
|
||||
showToast(context, S.current.tools_action_info_error_occurred(e));
|
||||
}
|
||||
state = state.copyWith(working: false);
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
}
|
||||
|
||||
Future<String> 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());
|
||||
}
|
||||
|
||||
/// 管理员模式运行 RSI 启动器
|
||||
Future _adminRSILauncher(BuildContext context) async {
|
||||
if (state.rsiLauncherInstalledPath == "") {
|
||||
showToast(context,
|
||||
S.current.tools_action_info_rsi_launcher_directory_not_found);
|
||||
}
|
||||
SystemHelper.checkAndLaunchRSILauncher(state.rsiLauncherInstalledPath);
|
||||
}
|
||||
|
||||
Future<void> _rsiLogFix(BuildContext context) async {
|
||||
state = state.copyWith(working: true);
|
||||
final path = await SCLoggerHelper.getLogFilePath();
|
||||
if (!await File(path!).exists()) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_log_file_not_exist);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SystemHelper.killRSILauncher();
|
||||
await File(path).delete(recursive: true);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_cleanup_complete);
|
||||
SystemHelper.checkAndLaunchRSILauncher(state.rsiLauncherInstalledPath);
|
||||
} catch (_) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_cleanup_failed(path));
|
||||
}
|
||||
|
||||
state = state.copyWith(working: false);
|
||||
}
|
||||
|
||||
openDir(path) async {
|
||||
SystemHelper.openDir(path);
|
||||
}
|
||||
|
||||
Future _showSystemInfo(BuildContext context) async {
|
||||
state = state.copyWith(working: true);
|
||||
final systemInfo = await getSystemInfo();
|
||||
if (!context.mounted) return;
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
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,
|
||||
),
|
||||
actions: [
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
|
||||
child: Text(S.current.action_close),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
state = state.copyWith(working: false);
|
||||
}
|
||||
|
||||
Future<void> _cleanShaderCache(BuildContext context) async {
|
||||
state = state.copyWith(working: true);
|
||||
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
final l =
|
||||
await Directory(gameShaderCachePath!).list(recursive: false).toList();
|
||||
for (var value in l) {
|
||||
if (value is Directory) {
|
||||
if (!value.absolute.path.contains("Crashes")) {
|
||||
await value.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
state = state.copyWith(working: false);
|
||||
}
|
||||
|
||||
Future<void> _downloadP4k(BuildContext context) async {
|
||||
String savePath = state.scInstalledPath;
|
||||
String fileName = "Data.p4k";
|
||||
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
await showToast(context, S.current.tools_action_info_p4k_file_description);
|
||||
try {
|
||||
state = state.copyWith(working: true);
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cManager
|
||||
.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c!;
|
||||
|
||||
// check download task list
|
||||
for (var value in [
|
||||
...await aria2c.tellActive(),
|
||||
...await aria2c.tellWaiting(0, 100000)
|
||||
]) {
|
||||
final t = HomeDownloaderUIModel.getTaskTypeAndName(value);
|
||||
if (t.key == "torrent" && t.value.contains("Data.p4k")) {
|
||||
if (!context.mounted) return;
|
||||
showToast(
|
||||
context, S.current.tools_action_info_p4k_download_in_progress);
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var torrentUrl = "";
|
||||
final l = await Api.getAppTorrentDataList();
|
||||
for (var torrent in l) {
|
||||
if (torrent.name == "Data.p4k") {
|
||||
torrentUrl = torrent.url!;
|
||||
}
|
||||
}
|
||||
if (torrentUrl == "") {
|
||||
state = state.copyWith(working: false);
|
||||
if (!context.mounted) return;
|
||||
showToast(
|
||||
context, S.current.tools_action_info_function_under_maintenance);
|
||||
return;
|
||||
}
|
||||
|
||||
final userSelect = await FilePicker.platform.saveFile(
|
||||
initialDirectory: savePath,
|
||||
fileName: fileName,
|
||||
lockParentWindow: true);
|
||||
if (userSelect == null) {
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
|
||||
savePath = userSelect;
|
||||
dPrint(savePath);
|
||||
|
||||
if (savePath.endsWith("\\$fileName")) {
|
||||
savePath = savePath.substring(0, savePath.length - fileName.length - 1);
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
final btData = await RSHttp.get(torrentUrl).unwrap(context: context);
|
||||
if (btData == null || btData.data == null) {
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
final b64Str = base64Encode(btData.data!);
|
||||
|
||||
final gid =
|
||||
await aria2c.addTorrent(b64Str, extraParams: {"dir": savePath});
|
||||
state = state.copyWith(working: false);
|
||||
dPrint("Aria2cManager.aria2c.addUri resp === $gid");
|
||||
await aria2c.saveSession();
|
||||
AnalyticsApi.touch("p4k_download");
|
||||
if (!context.mounted) return;
|
||||
context.push("/index/downloader");
|
||||
} catch (e) {
|
||||
state = state.copyWith(working: false);
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.app_init_failed_with_reason(e));
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
launchUrlString(
|
||||
"https://citizenwiki.cn/SC%E6%B1%89%E5%8C%96%E7%9B%92%E5%AD%90#%E5%88%86%E6%B5%81%E4%B8%8B%E8%BD%BD%E6%95%99%E7%A8%8B");
|
||||
}
|
||||
|
||||
Future<bool> _checkPhotographyStatus(BuildContext context,
|
||||
{bool? setMode}) async {
|
||||
final scInstalledPath = state.scInstalledPath;
|
||||
final keys = ["AudioShakeStrength", "CameraSpringMovement", "ShakeScale"];
|
||||
final attributesFile = File(
|
||||
"$scInstalledPath\\USER\\Client\\0\\Profiles\\default\\attributes.xml");
|
||||
if (setMode == null) {
|
||||
bool isEnable = false;
|
||||
if (scInstalledPath.isNotEmpty) {
|
||||
if (await attributesFile.exists()) {
|
||||
final xmlFile =
|
||||
XmlDocument.parse(await attributesFile.readAsString());
|
||||
isEnable = true;
|
||||
for (var k in keys) {
|
||||
if (!isEnable) break;
|
||||
final e = xmlFile.rootElement.children
|
||||
.where((element) => element.getAttribute("name") == k)
|
||||
.firstOrNull;
|
||||
if (e != null && e.getAttribute("value") == "0") {
|
||||
} else {
|
||||
isEnable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isEnable;
|
||||
} else {
|
||||
if (!await attributesFile.exists()) {
|
||||
if (!context.mounted) return false;
|
||||
showToast(context, S.current.tools_action_info_config_file_not_exist);
|
||||
return false;
|
||||
}
|
||||
final xmlFile = XmlDocument.parse(await attributesFile.readAsString());
|
||||
// clear all
|
||||
xmlFile.rootElement.children.removeWhere(
|
||||
(element) => keys.contains(element.getAttribute("name")));
|
||||
if (setMode) {
|
||||
for (var element in keys) {
|
||||
XmlElement newNode = XmlElement(XmlName('Attr'), [
|
||||
XmlAttribute(XmlName('name'), element),
|
||||
XmlAttribute(XmlName('value'), '0'),
|
||||
]);
|
||||
xmlFile.rootElement.children.add(newNode);
|
||||
}
|
||||
}
|
||||
dPrint(xmlFile);
|
||||
await attributesFile.delete();
|
||||
await attributesFile.writeAsString(xmlFile.toXmlString(pretty: true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_onChangePhotographyMode(BuildContext context, bool isEnable) async {
|
||||
_checkPhotographyStatus(context, setMode: !isEnable)
|
||||
.unwrap(context: context);
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
}
|
||||
|
||||
void onChangeGamePath(String v) {
|
||||
state = state.copyWith(scInstalledPath: v);
|
||||
}
|
||||
|
||||
void onChangeLauncherPath(String s) {
|
||||
state = state.copyWith(rsiLauncherInstalledPath: s);
|
||||
}
|
||||
|
||||
_doHostsBooster(BuildContext context) async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const HostsBoosterDialogUI());
|
||||
}
|
||||
|
||||
_unp4kc(BuildContext context) async {
|
||||
context.push("/tools/unp4kc");
|
||||
}
|
||||
|
||||
static 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));
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => RsiLauncherEnhanceDialogUI(
|
||||
showNotGameInstallMsg: showNotGameInstallMsg,
|
||||
));
|
||||
}
|
||||
}
|
@ -1,311 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'tools_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ToolsUIState {
|
||||
bool get working => throw _privateConstructorUsedError;
|
||||
String get scInstalledPath => throw _privateConstructorUsedError;
|
||||
String get rsiLauncherInstalledPath => throw _privateConstructorUsedError;
|
||||
List<String> get scInstallPaths => throw _privateConstructorUsedError;
|
||||
List<String> get rsiLauncherInstallPaths =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<ToolsItemData> get items => throw _privateConstructorUsedError;
|
||||
bool get isItemLoading => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ToolsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ToolsUIStateCopyWith<ToolsUIState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ToolsUIStateCopyWith<$Res> {
|
||||
factory $ToolsUIStateCopyWith(
|
||||
ToolsUIState value, $Res Function(ToolsUIState) then) =
|
||||
_$ToolsUIStateCopyWithImpl<$Res, ToolsUIState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool working,
|
||||
String scInstalledPath,
|
||||
String rsiLauncherInstalledPath,
|
||||
List<String> scInstallPaths,
|
||||
List<String> rsiLauncherInstallPaths,
|
||||
List<ToolsItemData> items,
|
||||
bool isItemLoading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ToolsUIStateCopyWithImpl<$Res, $Val extends ToolsUIState>
|
||||
implements $ToolsUIStateCopyWith<$Res> {
|
||||
_$ToolsUIStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ToolsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? working = null,
|
||||
Object? scInstalledPath = null,
|
||||
Object? rsiLauncherInstalledPath = null,
|
||||
Object? scInstallPaths = null,
|
||||
Object? rsiLauncherInstallPaths = null,
|
||||
Object? items = null,
|
||||
Object? isItemLoading = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
working: null == working
|
||||
? _value.working
|
||||
: working // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
scInstalledPath: null == scInstalledPath
|
||||
? _value.scInstalledPath
|
||||
: scInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
rsiLauncherInstalledPath: null == rsiLauncherInstalledPath
|
||||
? _value.rsiLauncherInstalledPath
|
||||
: rsiLauncherInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
scInstallPaths: null == scInstallPaths
|
||||
? _value.scInstallPaths
|
||||
: scInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
rsiLauncherInstallPaths: null == rsiLauncherInstallPaths
|
||||
? _value.rsiLauncherInstallPaths
|
||||
: rsiLauncherInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
items: null == items
|
||||
? _value.items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<ToolsItemData>,
|
||||
isItemLoading: null == isItemLoading
|
||||
? _value.isItemLoading
|
||||
: isItemLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ToolsUIStateImplCopyWith<$Res>
|
||||
implements $ToolsUIStateCopyWith<$Res> {
|
||||
factory _$$ToolsUIStateImplCopyWith(
|
||||
_$ToolsUIStateImpl value, $Res Function(_$ToolsUIStateImpl) then) =
|
||||
__$$ToolsUIStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool working,
|
||||
String scInstalledPath,
|
||||
String rsiLauncherInstalledPath,
|
||||
List<String> scInstallPaths,
|
||||
List<String> rsiLauncherInstallPaths,
|
||||
List<ToolsItemData> items,
|
||||
bool isItemLoading});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ToolsUIStateImplCopyWithImpl<$Res>
|
||||
extends _$ToolsUIStateCopyWithImpl<$Res, _$ToolsUIStateImpl>
|
||||
implements _$$ToolsUIStateImplCopyWith<$Res> {
|
||||
__$$ToolsUIStateImplCopyWithImpl(
|
||||
_$ToolsUIStateImpl _value, $Res Function(_$ToolsUIStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ToolsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? working = null,
|
||||
Object? scInstalledPath = null,
|
||||
Object? rsiLauncherInstalledPath = null,
|
||||
Object? scInstallPaths = null,
|
||||
Object? rsiLauncherInstallPaths = null,
|
||||
Object? items = null,
|
||||
Object? isItemLoading = null,
|
||||
}) {
|
||||
return _then(_$ToolsUIStateImpl(
|
||||
working: null == working
|
||||
? _value.working
|
||||
: working // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
scInstalledPath: null == scInstalledPath
|
||||
? _value.scInstalledPath
|
||||
: scInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
rsiLauncherInstalledPath: null == rsiLauncherInstalledPath
|
||||
? _value.rsiLauncherInstalledPath
|
||||
: rsiLauncherInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
scInstallPaths: null == scInstallPaths
|
||||
? _value._scInstallPaths
|
||||
: scInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
rsiLauncherInstallPaths: null == rsiLauncherInstallPaths
|
||||
? _value._rsiLauncherInstallPaths
|
||||
: rsiLauncherInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
items: null == items
|
||||
? _value._items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<ToolsItemData>,
|
||||
isItemLoading: null == isItemLoading
|
||||
? _value.isItemLoading
|
||||
: isItemLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ToolsUIStateImpl implements _ToolsUIState {
|
||||
_$ToolsUIStateImpl(
|
||||
{this.working = false,
|
||||
this.scInstalledPath = "",
|
||||
this.rsiLauncherInstalledPath = "",
|
||||
final List<String> scInstallPaths = const [],
|
||||
final List<String> rsiLauncherInstallPaths = const [],
|
||||
final List<ToolsItemData> items = const [],
|
||||
this.isItemLoading = false})
|
||||
: _scInstallPaths = scInstallPaths,
|
||||
_rsiLauncherInstallPaths = rsiLauncherInstallPaths,
|
||||
_items = items;
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool working;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String scInstalledPath;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String rsiLauncherInstalledPath;
|
||||
final List<String> _scInstallPaths;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get scInstallPaths {
|
||||
if (_scInstallPaths is EqualUnmodifiableListView) return _scInstallPaths;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_scInstallPaths);
|
||||
}
|
||||
|
||||
final List<String> _rsiLauncherInstallPaths;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get rsiLauncherInstallPaths {
|
||||
if (_rsiLauncherInstallPaths is EqualUnmodifiableListView)
|
||||
return _rsiLauncherInstallPaths;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_rsiLauncherInstallPaths);
|
||||
}
|
||||
|
||||
final List<ToolsItemData> _items;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<ToolsItemData> get items {
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_items);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isItemLoading;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ToolsUIState(working: $working, scInstalledPath: $scInstalledPath, rsiLauncherInstalledPath: $rsiLauncherInstalledPath, scInstallPaths: $scInstallPaths, rsiLauncherInstallPaths: $rsiLauncherInstallPaths, items: $items, isItemLoading: $isItemLoading)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ToolsUIStateImpl &&
|
||||
(identical(other.working, working) || other.working == working) &&
|
||||
(identical(other.scInstalledPath, scInstalledPath) ||
|
||||
other.scInstalledPath == scInstalledPath) &&
|
||||
(identical(
|
||||
other.rsiLauncherInstalledPath, rsiLauncherInstalledPath) ||
|
||||
other.rsiLauncherInstalledPath == rsiLauncherInstalledPath) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._scInstallPaths, _scInstallPaths) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other._rsiLauncherInstallPaths, _rsiLauncherInstallPaths) &&
|
||||
const DeepCollectionEquality().equals(other._items, _items) &&
|
||||
(identical(other.isItemLoading, isItemLoading) ||
|
||||
other.isItemLoading == isItemLoading));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
working,
|
||||
scInstalledPath,
|
||||
rsiLauncherInstalledPath,
|
||||
const DeepCollectionEquality().hash(_scInstallPaths),
|
||||
const DeepCollectionEquality().hash(_rsiLauncherInstallPaths),
|
||||
const DeepCollectionEquality().hash(_items),
|
||||
isItemLoading);
|
||||
|
||||
/// Create a copy of ToolsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ToolsUIStateImplCopyWith<_$ToolsUIStateImpl> get copyWith =>
|
||||
__$$ToolsUIStateImplCopyWithImpl<_$ToolsUIStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _ToolsUIState implements ToolsUIState {
|
||||
factory _ToolsUIState(
|
||||
{final bool working,
|
||||
final String scInstalledPath,
|
||||
final String rsiLauncherInstalledPath,
|
||||
final List<String> scInstallPaths,
|
||||
final List<String> rsiLauncherInstallPaths,
|
||||
final List<ToolsItemData> items,
|
||||
final bool isItemLoading}) = _$ToolsUIStateImpl;
|
||||
|
||||
@override
|
||||
bool get working;
|
||||
@override
|
||||
String get scInstalledPath;
|
||||
@override
|
||||
String get rsiLauncherInstalledPath;
|
||||
@override
|
||||
List<String> get scInstallPaths;
|
||||
@override
|
||||
List<String> get rsiLauncherInstallPaths;
|
||||
@override
|
||||
List<ToolsItemData> get items;
|
||||
@override
|
||||
bool get isItemLoading;
|
||||
|
||||
/// Create a copy of ToolsUIState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ToolsUIStateImplCopyWith<_$ToolsUIStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tools_ui_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$toolsUIModelHash() => r'b61ae444063db4c550fbf71e724eddd0f7104dc5';
|
||||
|
||||
/// See also [ToolsUIModel].
|
||||
@ProviderFor(ToolsUIModel)
|
||||
final toolsUIModelProvider =
|
||||
AutoDisposeNotifierProvider<ToolsUIModel, ToolsUIState>.internal(
|
||||
ToolsUIModel.new,
|
||||
name: r'toolsUIModelProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$toolsUIModelHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ToolsUIModel = AutoDisposeNotifier<ToolsUIState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -1,330 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_sizes/file_sizes.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:re_editor/re_editor.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
|
||||
import 'package:starcitizen_doctor/provider/unp4kc.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class UnP4kcUI extends HookConsumerWidget {
|
||||
const UnP4kcUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(unp4kCModelProvider);
|
||||
final model = ref.read(unp4kCModelProvider.notifier);
|
||||
final files = model.getFiles();
|
||||
final paths = state.curPath.trim().split("\\");
|
||||
|
||||
useEffect(() {
|
||||
AnalyticsApi.touch("unp4k_launch");
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
return makeDefaultPage(context,
|
||||
title: S.current.tools_unp4k_title(model.getGamePath()),
|
||||
useBodyContainer: false,
|
||||
content: makeBody(context, state, model, files, paths));
|
||||
}
|
||||
|
||||
Widget makeBody(BuildContext context, Unp4kcState state, Unp4kCModel model,
|
||||
List<AppUnp4kP4kItemData>? files, List<String> paths) {
|
||||
if (state.errorMessage.isNotEmpty) {
|
||||
return UnP4kErrorWidget(errorMessage: state.errorMessage);
|
||||
}
|
||||
return state.files == null
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: makeLoading(context)),
|
||||
if (state.endMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"${state.endMessage}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor.withOpacity(.06)),
|
||||
height: 36,
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: SuperListView.builder(
|
||||
itemCount: paths.length - 1,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var path = paths[index];
|
||||
if (path.isEmpty) {
|
||||
path = "\\";
|
||||
}
|
||||
final fullPath =
|
||||
"${paths.sublist(0, index + 1).join("\\")}\\";
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Text(path),
|
||||
onPressed: () {
|
||||
model.changeDir(fullPath, fullPath: true);
|
||||
},
|
||||
),
|
||||
const Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 12,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * .3,
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor.withOpacity(.01),
|
||||
),
|
||||
child: SuperListView.builder(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 6, bottom: 6, left: 3, right: 12),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = files![index];
|
||||
final fileName =
|
||||
item.name?.replaceAll(state.curPath.trim(), "") ??
|
||||
"?";
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withOpacity(.05),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (item.isDirectory ?? false) {
|
||||
model.changeDir(fileName);
|
||||
} else {
|
||||
model.openFile(item.name ?? "");
|
||||
}
|
||||
},
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
if (item.isDirectory ?? false)
|
||||
const Icon(
|
||||
FluentIcons.folder_fill,
|
||||
color: Color.fromRGBO(255, 224, 138, 1),
|
||||
)
|
||||
else if (fileName.endsWith(".xml"))
|
||||
const Icon(
|
||||
FluentIcons.file_code,
|
||||
)
|
||||
else
|
||||
const Icon(
|
||||
FluentIcons.open_file,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
fileName,
|
||||
style: const TextStyle(
|
||||
fontSize: 13),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!(item.isDirectory ?? true)) ...[
|
||||
const SizedBox(height: 1),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
FileSize.getSize(item.size),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white
|
||||
.withOpacity(.6)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
"${item.dateTime}",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white
|
||||
.withOpacity(.6)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 14,
|
||||
color: Colors.white.withOpacity(.6),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: files?.length ?? 0,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: state.tempOpenFile == null
|
||||
? Center(
|
||||
child: Text(S.current.tools_unp4k_view_file),
|
||||
)
|
||||
: state.tempOpenFile?.key == "loading"
|
||||
? makeLoading(context)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: [
|
||||
if (state.tempOpenFile?.key == "text")
|
||||
Expanded(
|
||||
child: _TextTempWidget(
|
||||
state.tempOpenFile?.value ?? ""))
|
||||
else
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current
|
||||
.tools_unp4k_msg_unknown_file_type(
|
||||
state.tempOpenFile
|
||||
?.value ??
|
||||
"")),
|
||||
const SizedBox(height: 32),
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(4),
|
||||
child: Text(S.current
|
||||
.action_open_folder),
|
||||
),
|
||||
onPressed: () {
|
||||
SystemHelper.openDir(state
|
||||
.tempOpenFile
|
||||
?.value ??
|
||||
"");
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
)),
|
||||
if (state.endMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"${state.endMessage}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TextTempWidget extends HookConsumerWidget {
|
||||
final String filePath;
|
||||
|
||||
const _TextTempWidget(this.filePath);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final textData = useState<String?>(null);
|
||||
|
||||
useEffect(() {
|
||||
File(filePath).readAsString().then((value) {
|
||||
textData.value = value;
|
||||
}).catchError((err) {
|
||||
textData.value = "Error: $err";
|
||||
});
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
if (textData.value == null) return makeLoading(context);
|
||||
|
||||
return CodeEditor(
|
||||
controller: CodeLineEditingController.fromText('${textData.value}'),
|
||||
readOnly: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UnP4kErrorWidget extends StatelessWidget {
|
||||
final String errorMessage;
|
||||
|
||||
const UnP4kErrorWidget({super.key, required this.errorMessage});
|
||||
|
||||
static const _downloadUrl =
|
||||
"https://aka.ms/dotnet-core-applaunch?missing_runtime=true&arch=x64&rid=win-x64&os=win10&apphost_version=8.0.0";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (Unp4kCModel.checkRunTimeError(errorMessage)) ...[
|
||||
Text(
|
||||
S.current.tools_unp4k_missing_runtime,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(S.current.tools_unp4k_missing_runtime_info),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
||||
child: Text(
|
||||
S.current.tools_unp4k_missing_runtime_action_install),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrlString(_downloadUrl);
|
||||
}),
|
||||
] else
|
||||
Text(errorMessage),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,304 +0,0 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.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/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/data/app_version_data.dart';
|
||||
import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart';
|
||||
|
||||
typedef RsiLoginCallback = void Function(Map? data, bool success);
|
||||
|
||||
class WebViewModel {
|
||||
late Webview webview;
|
||||
final BuildContext context;
|
||||
|
||||
bool _isClosed = false;
|
||||
|
||||
bool get isClosed => _isClosed;
|
||||
|
||||
WebViewModel(this.context,
|
||||
{this.loginMode = false, this.loginCallback, this.loginChannel = "LIVE"});
|
||||
|
||||
String url = "";
|
||||
bool canGoBack = false;
|
||||
|
||||
final localizationResource = <String, dynamic>{};
|
||||
|
||||
var localizationScript = "";
|
||||
|
||||
bool enableCapture = false;
|
||||
|
||||
bool isEnableToolSiteMirrors = false;
|
||||
|
||||
Map<String, String>? _curReplaceWords;
|
||||
|
||||
Map<String, String>? get curReplaceWords => _curReplaceWords;
|
||||
|
||||
final bool loginMode;
|
||||
final String loginChannel;
|
||||
|
||||
bool _loginModeSuccess = false;
|
||||
|
||||
final RsiLoginCallback? loginCallback;
|
||||
|
||||
initWebView(
|
||||
{String title = "",
|
||||
required String applicationSupportDir,
|
||||
required AppVersionData appVersionData}) async {
|
||||
try {
|
||||
final userBox = await Hive.openBox("app_conf");
|
||||
isEnableToolSiteMirrors =
|
||||
userBox.get("isEnableToolSiteMirrors", defaultValue: false);
|
||||
webview = await WebviewWindow.create(
|
||||
configuration: CreateConfiguration(
|
||||
windowWidth: loginMode ? 960 : 1920,
|
||||
windowHeight: loginMode ? 720 : 1080,
|
||||
userDataFolderWindows: "$applicationSupportDir/webview_data",
|
||||
title: Platform.isMacOS ? "" : title));
|
||||
// webview.openDevToolsWindow();
|
||||
webview.isNavigating.addListener(() async {
|
||||
if (!webview.isNavigating.value && localizationResource.isNotEmpty) {
|
||||
dPrint("webview Navigating url === $url");
|
||||
if (url.contains("robertsspaceindustries.com")) {
|
||||
// SC 官网
|
||||
dPrint("load script");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await webview.evaluateJavaScript(localizationScript);
|
||||
dPrint("update replaceWords");
|
||||
final replaceWords = _getLocalizationResource("zh-CN");
|
||||
|
||||
const org = "https://robertsspaceindustries.com/orgs";
|
||||
const citizens = "https://robertsspaceindustries.com/citizens";
|
||||
const organization =
|
||||
"https://robertsspaceindustries.com/account/organization";
|
||||
const concierge =
|
||||
"https://robertsspaceindustries.com/account/concierge";
|
||||
const referral =
|
||||
"https://robertsspaceindustries.com/account/referral-program";
|
||||
const address =
|
||||
"https://robertsspaceindustries.com/account/addresses";
|
||||
|
||||
const hangar = "https://robertsspaceindustries.com/account/pledges";
|
||||
|
||||
const spectrum =
|
||||
"https://robertsspaceindustries.com/spectrum/community/";
|
||||
// 跳过光谱论坛 https://github.com/StarCitizenToolBox/StarCitizenBoxBrowserEx/issues/1
|
||||
if (url.startsWith(spectrum)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.startsWith(org) ||
|
||||
url.startsWith(citizens) ||
|
||||
url.startsWith(organization)) {
|
||||
replaceWords.add({
|
||||
"word": 'members',
|
||||
"replacement": S.current.webview_localization_name_member
|
||||
});
|
||||
replaceWords.addAll(_getLocalizationResource("orgs"));
|
||||
}
|
||||
|
||||
if (address.startsWith(address)) {
|
||||
replaceWords.addAll(_getLocalizationResource("address"));
|
||||
}
|
||||
|
||||
if (url.startsWith(referral)) {
|
||||
replaceWords.addAll([
|
||||
{
|
||||
"word": 'Total recruits: ',
|
||||
"replacement":
|
||||
S.current.webview_localization_total_invitations
|
||||
},
|
||||
{
|
||||
"word": 'Prospects ',
|
||||
"replacement":
|
||||
S.current.webview_localization_unfinished_invitations
|
||||
},
|
||||
{
|
||||
"word": 'Recruits',
|
||||
"replacement":
|
||||
S.current.webview_localization_finished_invitations
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (url.startsWith(concierge)) {
|
||||
replaceWords.clear();
|
||||
replaceWords.addAll(_getLocalizationResource("concierge"));
|
||||
}
|
||||
if (url.startsWith(hangar)) {
|
||||
replaceWords.addAll(_getLocalizationResource("hangar"));
|
||||
}
|
||||
|
||||
_curReplaceWords = {};
|
||||
for (var element in replaceWords) {
|
||||
_curReplaceWords?[element["word"] ?? ""] =
|
||||
element["replacement"] ?? "";
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await webview.evaluateJavaScript(
|
||||
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||
|
||||
/// loginMode
|
||||
if (loginMode) {
|
||||
dPrint(
|
||||
"--- do rsi login ---\n run === getRSILauncherToken(\"$loginChannel\");");
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
webview.evaluateJavaScript(
|
||||
"getRSILauncherToken(\"$loginChannel\");");
|
||||
}
|
||||
} else if (url.startsWith(await _handleMirrorsUrl(
|
||||
"https://www.erkul.games", appVersionData))) {
|
||||
dPrint("load script");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await webview.evaluateJavaScript(localizationScript);
|
||||
dPrint("update replaceWords");
|
||||
final replaceWords = _getLocalizationResource("DPS");
|
||||
await webview.evaluateJavaScript(
|
||||
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||
} else if (url.startsWith(await _handleMirrorsUrl(
|
||||
"https://uexcorp.space", appVersionData))) {
|
||||
dPrint("load script");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await webview.evaluateJavaScript(localizationScript);
|
||||
dPrint("update replaceWords");
|
||||
final replaceWords = _getLocalizationResource("UEX");
|
||||
await webview.evaluateJavaScript(
|
||||
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||
}
|
||||
}
|
||||
});
|
||||
webview.addOnUrlRequestCallback((url) {
|
||||
dPrint("OnUrlRequestCallback === $url");
|
||||
this.url = url;
|
||||
});
|
||||
webview.onClose.whenComplete(dispose);
|
||||
if (loginMode) {
|
||||
webview.addOnWebMessageReceivedCallback((messageString) {
|
||||
final message = json.decode(messageString);
|
||||
if (message["action"] == "webview_rsi_login_show_window") {
|
||||
webview.setWebviewWindowVisibility(true);
|
||||
} else if (message["action"] == "webview_rsi_login_success") {
|
||||
_loginModeSuccess = true;
|
||||
loginCallback?.call(message, true);
|
||||
webview.close();
|
||||
}
|
||||
});
|
||||
Future.delayed(const Duration(seconds: 1))
|
||||
.then((value) => {webview.setWebviewWindowVisibility(false)});
|
||||
}
|
||||
} catch (e) {
|
||||
showToast(context, S.current.app_init_failed_with_reason(e));
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _handleMirrorsUrl(
|
||||
String url, AppVersionData appVersionData) async {
|
||||
var finalUrl = url;
|
||||
if (isEnableToolSiteMirrors) {
|
||||
for (var kv in appVersionData.webMirrors!.entries) {
|
||||
if (url.startsWith(kv.key)) {
|
||||
finalUrl = url.replaceFirst(kv.key, kv.value);
|
||||
AnalyticsApi.touch("webLocalization_with_boost_mirror");
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalUrl;
|
||||
}
|
||||
|
||||
launch(String url, AppVersionData appVersionData) async {
|
||||
webview.launch(await _handleMirrorsUrl(url, appVersionData));
|
||||
}
|
||||
|
||||
initLocalization(AppWebLocalizationVersionsData v) async {
|
||||
localizationScript = await rootBundle.loadString('assets/web_script.js');
|
||||
|
||||
/// https://github.com/CxJuice/Uex_Chinese_Translate
|
||||
// get versions
|
||||
final hostUrl = URLConf.webTranslateHomeUrl;
|
||||
dPrint("AppWebLocalizationVersionsData === ${v.toJson()}");
|
||||
|
||||
localizationResource["zh-CN"] = await _getJson("$hostUrl/zh-CN-rsi.json",
|
||||
cacheKey: "rsi", version: v.rsi);
|
||||
localizationResource["concierge"] = await _getJson(
|
||||
"$hostUrl/concierge.json",
|
||||
cacheKey: "concierge",
|
||||
version: v.concierge);
|
||||
localizationResource["orgs"] =
|
||||
await _getJson("$hostUrl/orgs.json", cacheKey: "orgs", version: v.orgs);
|
||||
localizationResource["address"] = await _getJson("$hostUrl/addresses.json",
|
||||
cacheKey: "addresses", version: v.addresses);
|
||||
localizationResource["hangar"] = await _getJson("$hostUrl/hangar.json",
|
||||
cacheKey: "hangar", version: v.hangar);
|
||||
localizationResource["UEX"] = await _getJson("$hostUrl/zh-CN-uex.json",
|
||||
cacheKey: "uex", version: v.uex);
|
||||
localizationResource["DPS"] = await _getJson("$hostUrl/zh-CN-dps.json",
|
||||
cacheKey: "dps", version: v.dps);
|
||||
}
|
||||
|
||||
List<Map<String, String>> _getLocalizationResource(String key) {
|
||||
final List<Map<String, String>> localizations = [];
|
||||
final dict = localizationResource[key];
|
||||
if (dict is Map) {
|
||||
for (var element in dict.entries) {
|
||||
final k = element.key
|
||||
.toString()
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll(RegExp("/\xa0/g"), ' ')
|
||||
.replaceAll(RegExp("/s{2,}/g"), ' ');
|
||||
localizations
|
||||
.add({"word": k, "replacement": element.value.toString().trim()});
|
||||
}
|
||||
}
|
||||
return localizations;
|
||||
}
|
||||
|
||||
Future<Map> _getJson(String url,
|
||||
{String cacheKey = "", String? version}) async {
|
||||
final box = await Hive.openBox("web_localization_cache_data");
|
||||
if (cacheKey.isNotEmpty) {
|
||||
final localVersion = box.get("${cacheKey}_version}", defaultValue: "");
|
||||
var data = box.get(cacheKey, defaultValue: {});
|
||||
if (data is Map && data.isNotEmpty && localVersion == version) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
final startTime = DateTime.now();
|
||||
final r = await RSHttp.getText(url);
|
||||
final endTime = DateTime.now();
|
||||
final data = json.decode(r);
|
||||
if (cacheKey.isNotEmpty) {
|
||||
dPrint(
|
||||
"update $cacheKey v == $version time == ${(endTime.microsecondsSinceEpoch - startTime.microsecondsSinceEpoch) / 1000 / 1000}s");
|
||||
await box.put(cacheKey, data);
|
||||
await box.put("${cacheKey}_version}", version);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void addOnWebMessageReceivedCallback(OnWebMessageReceivedCallback callback) {
|
||||
webview.addOnWebMessageReceivedCallback(callback);
|
||||
}
|
||||
|
||||
void removeOnWebMessageReceivedCallback(
|
||||
OnWebMessageReceivedCallback callback) {
|
||||
webview.removeOnWebMessageReceivedCallback(callback);
|
||||
}
|
||||
|
||||
FutureOr<void> dispose() {
|
||||
if (loginMode && !_loginModeSuccess) {
|
||||
loginCallback?.call(null, false);
|
||||
}
|
||||
_isClosed = true;
|
||||
}
|
||||
}
|
37
lib/widgets/src/blur_oval_widget.dart
Normal file
37
lib/widgets/src/blur_oval_widget.dart
Normal file
@ -0,0 +1,37 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:markdown_widget/config/all.dart';
|
||||
import 'package:markdown_widget/widget/all.dart';
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
@ -43,28 +42,24 @@ Widget makeDefaultPage(BuildContext context,
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||
title: DragToMoveArea(
|
||||
child: titleRow ??
|
||||
Column(
|
||||
title: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
)
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [...?actions, const WindowButtons()],
|
||||
children: [...?actions],
|
||||
)),
|
||||
content: useBodyContainer
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
),
|
||||
child: content,
|
||||
@ -73,23 +68,6 @@ Widget makeDefaultPage(BuildContext context,
|
||||
);
|
||||
}
|
||||
|
||||
class WindowButtons extends StatelessWidget {
|
||||
const WindowButtons({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final FluentThemeData theme = FluentTheme.of(context);
|
||||
return SizedBox(
|
||||
width: 138,
|
||||
height: 50,
|
||||
child: WindowCaption(
|
||||
brightness: theme.brightness,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> makeMarkdownView(String description, {String? attachmentsUrl}) {
|
||||
return MarkdownGenerator().buildWidgets(description,
|
||||
config: MarkdownConfig(configs: [
|
||||
|
Reference in New Issue
Block a user