初始化登录功能

This commit is contained in:
xkeyC 2023-10-28 13:00:10 +08:00
parent 59412db924
commit 6153bcf9c0
8 changed files with 172 additions and 66 deletions

View File

@ -24,6 +24,7 @@ linter:
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
dangling_library_doc_comments: false
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options

View File

@ -1,6 +1,6 @@
/// https://github.com/CxJuice/Uex_Chinese_Translate /// https://github.com/CxJuice/Uex_Chinese_Translate
/// ------- WebLocalization Script --------------
let SCLocalizationReplaceLocalesMap = {}; let SCLocalizationReplaceLocalesMap = {};
let enable_webview_localization_capture = false; let enable_webview_localization_capture = false;
let SCLocalizationEnableSplitMode = false; let SCLocalizationEnableSplitMode = false;
@ -216,4 +216,68 @@ function ReportUnTranslate(k, v) {
} }
} }
InitWebLocalization(); InitWebLocalization();
/// ----- Login Script ----
async function getRSILauncherToken() {
// check login
let r = await fetch("api/launcher/v3/account/check", {
method: 'POST', headers: {
'x-rsi-token': $.cookie('Rsi-Token'),
},
});
if (r.status !== 200) {
// wait login
return;
}
// get claims
let claimsR = await fetch("api/launcher/v3/games/claims", {
method: 'POST', headers: {
'x-rsi-token': $.cookie('Rsi-Token'),
},
});
if (claimsR.status !== 200) return;
let claimsData = (await claimsR.json())["data"];
let tokenFormData = new FormData();
tokenFormData.append('claims', claimsData);
tokenFormData.append('gameId', 'SC');
let tokenR = await fetch("api/launcher/v3/games/token", {
method: 'POST', headers: {
'x-rsi-token': $.cookie('Rsi-Token'),
},
body: tokenFormData
});
if (tokenR.status !== 200) return;
let TokenData = (await tokenR.json())["data"];
console.log(TokenData);
// get release Data
let releaseFormData = new FormData();
releaseFormData.append("channelId", "LIVE");
releaseFormData.append("claims", claimsData);
releaseFormData.append("gameId", "SC");
releaseFormData.append("platformId", "prod");
let releaseR = await fetch("api/launcher/v3/games/release", {
method: 'POST', headers: {
'x-rsi-token': $.cookie('Rsi-Token'),
},
body: releaseFormData
});
if (releaseR.status !== 200) return;
let releaseDataJson = await releaseR.json();
console.log(releaseDataJson);
// post message
window.chrome.webview.postMessage({
action: 'webview_rsi_login_success', data: {
'webToken': $.cookie('Rsi-Token'),
'claims': claimsData,
'authToken': TokenData,
'releaseInfo': releaseDataJson
}
});
}

View File

@ -16,8 +16,7 @@ class BaseUIContainer extends ConsumerStatefulWidget {
final dynamic Function() modelCreate; final dynamic Function() modelCreate;
const BaseUIContainer( const BaseUIContainer(
{Key? key, required this.uiCreate, required this.modelCreate}) {super.key, required this.uiCreate, required this.modelCreate});
: super(key: key);
@override @override
// ignore: no_logic_in_create_state // ignore: no_logic_in_create_state

View File

@ -51,7 +51,7 @@ class AppUI extends BaseUI {
} }
class WindowButtons extends StatelessWidget { class WindowButtons extends StatelessWidget {
const WindowButtons({Key? key}) : super(key: key); const WindowButtons({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -388,9 +388,12 @@ class HomeUIModel extends BaseUIModel {
notifyListeners(); notifyListeners();
} }
goWebView(String title, String url, {bool useLocalization = false}) async { goWebView(String title, String url,
{bool useLocalization = false,
bool loginMode = false,
RsiLoginCallback? rsiLoginCallback}) async {
if (useLocalization) { if (useLocalization) {
const tipVersion = 1; const tipVersion = 2;
final box = await Hive.openBox("app_conf"); final box = await Hive.openBox("app_conf");
final skip = final skip =
await box.get("skip_web_localization_tip_version", defaultValue: 0); await box.get("skip_web_localization_tip_version", defaultValue: 0);
@ -399,15 +402,18 @@ class HomeUIModel extends BaseUIModel {
context!, context!,
"星际公民官网汉化", "星际公民官网汉化",
const Text( const Text(
"该汉化功能移植自星际公民汉化组的 Tampermonkey 浏览器插件https://greasyfork.org/zh-CN/scripts/459084文本内容由星际公民汉化组进行更新。"
"\n\n移植后的脚本源代码随 StarCitizenDoctor 项目一起分发https://jihulab.com/StarCitizenCN_Community/StarCitizenDoctor"
"\n\n\n本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!" "\n\n\n本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!"
"\n\n\n使用此功能登录账号时请确保您的 StarCitizenDoctor 是从可信任的来源下载。", "\n\n\n使用此功能登录账号时请确保您的 StarCitizenDoctor 是从可信任的来源下载。",
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: MediaQuery.of(context!).size.width * .6)); maxWidth: MediaQuery.of(context!).size.width * .6));
if (!ok) return; if (!ok) {
if (loginMode) {
rsiLoginCallback?.call(null, false);
}
return;
}
await box.put("skip_web_localization_tip_version", tipVersion); await box.put("skip_web_localization_tip_version", tipVersion);
} }
} }
@ -417,7 +423,8 @@ class HomeUIModel extends BaseUIModel {
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/"); "https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
return; return;
} }
final webViewModel = WebViewModel(context!); final webViewModel = WebViewModel(context!,
loginMode: loginMode, loginCallback: rsiLoginCallback);
if (useLocalization) { if (useLocalization) {
isFixingString = "正在初始化汉化资源..."; isFixingString = "正在初始化汉化资源...";
isFixing = true; isFixing = true;
@ -431,7 +438,9 @@ class HomeUIModel extends BaseUIModel {
isFixing = false; isFixing = false;
} }
await webViewModel.initWebView(title: title); await webViewModel.initWebView(
title: title,
);
if (await File( if (await File(
"${AppConf.applicationSupportDir}\\webview_data\\enable_webview_localization_capture") "${AppConf.applicationSupportDir}\\webview_data\\enable_webview_localization_capture")
.exists()) { .exists()) {
@ -453,17 +462,19 @@ class HomeUIModel extends BaseUIModel {
launchRSI() async { launchRSI() async {
isRsiLauncherStarting = true; isRsiLauncherStarting = true;
notifyListeners(); notifyListeners();
final rsiLauncherInstalledPath = await SystemHelper.getRSILauncherPath(); // final rsiLauncherInstalledPath = await SystemHelper.getRSILauncherPath();
if (rsiLauncherInstalledPath.isEmpty) { // if (rsiLauncherInstalledPath.isEmpty) {
// isRsiLauncherStarting = false;
// notifyListeners();
// showToast(context!, "未找到 RSI 启动器目录");
// return;
// }
// SystemHelper.checkAndLaunchRSILauncher(rsiLauncherInstalledPath);
goWebView("登录 RSI 账户", "https://robertsspaceindustries.com/connect",
loginMode: true, rsiLoginCallback: (data, ok) {
isRsiLauncherStarting = false; isRsiLauncherStarting = false;
notifyListeners(); notifyListeners();
showToast(context!, "未找到 RSI 启动器目录"); }, useLocalization: true);
return;
}
SystemHelper.checkAndLaunchRSILauncher(rsiLauncherInstalledPath);
await Future.delayed(const Duration(seconds: 3));
isRsiLauncherStarting = false;
notifyListeners();
} }
bool isRSIServerStatusOK(Map map) { bool isRSIServerStatusOK(Map map) {

View File

@ -1,5 +1,6 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:desktop_webview_window/desktop_webview_window.dart'; import 'package:desktop_webview_window/desktop_webview_window.dart';
@ -12,6 +13,8 @@ import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart'
import '../../../api/api.dart'; import '../../../api/api.dart';
import '../../../base/ui.dart'; import '../../../base/ui.dart';
typedef RsiLoginCallback = void Function(Map? data, bool success);
class WebViewModel { class WebViewModel {
late Webview webview; late Webview webview;
final BuildContext context; final BuildContext context;
@ -20,7 +23,7 @@ class WebViewModel {
bool get isClosed => _isClosed; bool get isClosed => _isClosed;
WebViewModel(this.context); WebViewModel(this.context, {this.loginMode = false, this.loginCallback});
String url = ""; String url = "";
bool canGoBack = false; bool canGoBack = false;
@ -35,19 +38,27 @@ class WebViewModel {
Map<String, String>? get curReplaceWords => _curReplaceWords; Map<String, String>? get curReplaceWords => _curReplaceWords;
final bool loginMode;
bool _loginModeSuccess = false;
final RsiLoginCallback? loginCallback;
initWebView({String title = ""}) async { initWebView({String title = ""}) async {
try { try {
webview = await WebviewWindow.create( webview = await WebviewWindow.create(
configuration: CreateConfiguration( configuration: CreateConfiguration(
windowWidth: 1920, windowWidth: loginMode ? 960 : 1920,
windowHeight: 1080, windowHeight: loginMode ? 720 : 1080,
userDataFolderWindows: userDataFolderWindows:
"${AppConf.applicationSupportDir}/webview_data", "${AppConf.applicationSupportDir}/webview_data",
title: title)); title: title));
// webview.openDevToolsWindow(); // webview.openDevToolsWindow();
webview.isNavigating.addListener(() async { webview.isNavigating.addListener(() async {
if (!webview.isNavigating.value && localizationResource.isNotEmpty) { if (!webview.isNavigating.value && localizationResource.isNotEmpty) {
final uri = Uri.parse(url); final uri = Uri.parse(url);
dPrint("webview Navigating uri === $uri");
if (uri.host.contains("robertsspaceindustries.com")) { if (uri.host.contains("robertsspaceindustries.com")) {
// SC // SC
dPrint("load script"); dPrint("load script");
@ -103,6 +114,11 @@ class WebViewModel {
await Future.delayed(const Duration(milliseconds: 100)); await Future.delayed(const Duration(milliseconds: 100));
await webview.evaluateJavaScript( await webview.evaluateJavaScript(
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)"); "WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
if (loginMode) {
dPrint("--- do rsi login ---");
await Future.delayed(const Duration(milliseconds: 200));
webview.evaluateJavaScript("getRSILauncherToken();");
}
} else if (uri.host.contains("www.erkul.games") || } else if (uri.host.contains("www.erkul.games") ||
uri.host.contains("uexcorp.space") || uri.host.contains("uexcorp.space") ||
uri.host.contains("ccugame.app")) { uri.host.contains("ccugame.app")) {
@ -121,9 +137,17 @@ class WebViewModel {
dPrint("OnUrlRequestCallback === $url"); dPrint("OnUrlRequestCallback === $url");
this.url = url; this.url = url;
}); });
webview.onClose.whenComplete(() { webview.onClose.whenComplete(dispose);
_isClosed = true; if (loginMode) {
}); webview.addOnWebMessageReceivedCallback((messageString) {
final message = json.decode(messageString);
if (message["action"] == "webview_rsi_login_success") {
_loginModeSuccess = true;
loginCallback?.call(message, true);
webview.close();
}
});
}
} catch (e) { } catch (e) {
showToast(context, "初始化失败:$e"); showToast(context, "初始化失败:$e");
} }
@ -211,4 +235,11 @@ class WebViewModel {
OnWebMessageReceivedCallback callback) { OnWebMessageReceivedCallback callback) {
webview.removeOnWebMessageReceivedCallback(callback); webview.removeOnWebMessageReceivedCallback(callback);
} }
FutureOr<void> dispose() {
if (loginMode && !_loginModeSuccess) {
loginCallback?.call(null, false);
}
_isClosed = true;
}
} }

View File

@ -60,45 +60,45 @@ fastPadding(
} }
List<Widget> makeMarkdownView(String description) { List<Widget> makeMarkdownView(String description) {
return MarkdownGenerator( return MarkdownGenerator().buildWidgets(description,
config: MarkdownConfig(configs: [ config: MarkdownConfig(configs: [
LinkConfig(onTap: (url) { LinkConfig(onTap: (url) {
if (url.startsWith("/")) { if (url.startsWith("/")) {
url = "${AppConf.gitlabHomeUrl}/$url"; url = "${AppConf.gitlabHomeUrl}/$url";
}
launchUrlString(url);
}),
ImgConfig(builder: (String url, Map<String, String> attributes) {
return ExtendedImage.network(
url,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: [
ProgressRing(),
SizedBox(
height: 12,
),
Text("加载图片...")
],
),
),
);
case LoadState.completed:
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
);
case LoadState.failed:
return const Text("Loading Image error");
} }
}, launchUrlString(url);
); }),
}) ImgConfig(builder: (String url, Map<String, String> attributes) {
])).buildWidgets(description); return ExtendedImage.network(
url,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: [
ProgressRing(),
SizedBox(
height: 12,
),
Text("加载图片...")
],
),
),
);
case LoadState.completed:
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
);
case LoadState.failed:
return const Text("Loading Image error");
}
},
);
})
]));
} }
class NoScrollBehavior extends ScrollBehavior { class NoScrollBehavior extends ScrollBehavior {

View File

@ -44,7 +44,7 @@ dependencies:
markdown_widget: ^2.2.0 markdown_widget: ^2.2.0
extended_image: ^8.1.1 extended_image: ^8.1.1
device_info_plus: ^9.0.3 device_info_plus: ^9.0.3
file_picker: ^5.5.0 file_picker: ^6.0.0
file_sizes: ^1.0.6 file_sizes: ^1.0.6
desktop_webview_window: ^0.2.3 desktop_webview_window: ^0.2.3
flutter_svg: ^2.0.7 flutter_svg: ^2.0.7
@ -59,7 +59,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^2.0.0 flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec