mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 01:53:41 +08:00
feat: web 输入支持
This commit is contained in:
parent
1681e2407b
commit
472fdb08fb
53
assets/web/input_method/index.html
Normal file
53
assets/web/input_method/index.html
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no"/>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="style/mdui2.css">
|
||||||
|
<link href="style/google_icons.css" rel="stylesheet">
|
||||||
|
<script src="js/mdui2.global.js"></script>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
<title>SCToolBox Community Input Method Web</title>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="mdui-theme-light" style="position: relative;overflow: hidden">
|
||||||
|
<mdui-top-app-bar
|
||||||
|
scroll-behavior="shrink"
|
||||||
|
scroll-threshold="30"
|
||||||
|
scroll-target=".scroll-behavior-shrink">
|
||||||
|
|
||||||
|
<mdui-top-app-bar-title>SC汉化盒子社区输入法</mdui-top-app-bar-title>
|
||||||
|
<div style="flex-grow: 1"></div>
|
||||||
|
|
||||||
|
<mdui-button-icon icon="help" onclick="onShowHelp()"></mdui-button-icon>
|
||||||
|
|
||||||
|
|
||||||
|
</mdui-top-app-bar>
|
||||||
|
|
||||||
|
<div class="scroll-behavior-shrink" style="overflow: auto;">
|
||||||
|
|
||||||
|
<mdui-text-field id="input_message" style="padding-top: 6pt;padding-bottom: 6pt" rows="6" variant="outlined"
|
||||||
|
label="输入消息..."></mdui-text-field>
|
||||||
|
|
||||||
|
<div style="text-align: end">
|
||||||
|
<mdui-checkbox id="auto_copy" checked>自动复制</mdui-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mdui-button id="send_button" icon="send" onclick="onSendMessage()" full-width>发送</mdui-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mdui-snackbar id="snackbar_message" auto-close-delay="1000">Text</mdui-snackbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
57
assets/web/input_method/js/main.js
Normal file
57
assets/web/input_method/js/main.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
async function init() {
|
||||||
|
try {
|
||||||
|
let response = await fetch("/api");
|
||||||
|
let responseJson = await response.json();
|
||||||
|
if (responseJson.status === "ok") {
|
||||||
|
showMessage("服务连接成功!");
|
||||||
|
} else {
|
||||||
|
showMessage("服务连接失败!" + responseJson);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showMessage("服务连接失败!" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function onSendMessage() {
|
||||||
|
let send_button = document.getElementById("send_button");
|
||||||
|
let input = document.getElementById("input_message");
|
||||||
|
let isAutoCopy = document.getElementById("auto_copy").checked;
|
||||||
|
let isAutoSend = document.getElementById("auto_send").checked;
|
||||||
|
let messageJson = {
|
||||||
|
"text": input.value,
|
||||||
|
"autoCopy": isAutoCopy,
|
||||||
|
"autoInput": isAutoSend
|
||||||
|
};
|
||||||
|
send_button.loading = true;
|
||||||
|
try {
|
||||||
|
let response = await fetch("/api/send", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(messageJson)
|
||||||
|
});
|
||||||
|
let responseJson = await response.json();
|
||||||
|
console.log(responseJson);
|
||||||
|
showMessage(responseJson.message);
|
||||||
|
if (response.ok) {
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showMessage("发送失败!" + e);
|
||||||
|
}
|
||||||
|
send_button.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(message) {
|
||||||
|
let snack = document.getElementById("snackbar_message");
|
||||||
|
snack.open = false;
|
||||||
|
snack.innerText = message;
|
||||||
|
snack.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowHelp() {
|
||||||
|
alert("在浏览器中输入文本,将发送给汉化盒子转码。" +
|
||||||
|
"\n\n自动复制:勾选后自动复制转码结果到剪贴板。");
|
||||||
|
}
|
22
assets/web/input_method/js/mdui2.global.js
Normal file
22
assets/web/input_method/js/mdui2.global.js
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/web/input_method/style/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
BIN
assets/web/input_method/style/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
Normal file
Binary file not shown.
23
assets/web/input_method/style/google_icons.css
Normal file
23
assets/web/input_method/style/google_icons.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* fallback */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url(flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
1
assets/web/input_method/style/mdui2.css
Normal file
1
assets/web/input_method/style/mdui2.css
Normal file
File diff suppressed because one or more lines are too long
@ -6,4 +6,5 @@ class ConstConf {
|
|||||||
static const isMSE =
|
static const isMSE =
|
||||||
String.fromEnvironment("MSE", defaultValue: "false") == "true";
|
String.fromEnvironment("MSE", defaultValue: "false") == "true";
|
||||||
static const dohAddress = "https://223.6.6.6/resolve";
|
static const dohAddress = "https://223.6.6.6/resolve";
|
||||||
|
static const inputMethodServerPort = 59399;
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,11 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:starcitizen_doctor/ui/home/input_method/input_method_dialog_ui_model.dart';
|
import 'package:starcitizen_doctor/ui/home/input_method/input_method_dialog_ui_model.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/input_method/server.dart';
|
||||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
|
|
||||||
|
import 'server_qr_dialog_ui.dart';
|
||||||
|
|
||||||
class InputMethodDialogUI extends HookConsumerWidget {
|
class InputMethodDialogUI extends HookConsumerWidget {
|
||||||
const InputMethodDialogUI({super.key});
|
const InputMethodDialogUI({super.key});
|
||||||
|
|
||||||
@ -13,9 +16,16 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final state = ref.watch(inputMethodDialogUIModelProvider);
|
final state = ref.watch(inputMethodDialogUIModelProvider);
|
||||||
final model = ref.read(inputMethodDialogUIModelProvider.notifier);
|
final model = ref.read(inputMethodDialogUIModelProvider.notifier);
|
||||||
|
final serverState = ref.watch(inputMethodServerProvider);
|
||||||
|
final serverModel = ref.read(inputMethodServerProvider.notifier);
|
||||||
final srcTextCtrl = useTextEditingController();
|
final srcTextCtrl = useTextEditingController();
|
||||||
final destTextCtrl = useTextEditingController();
|
final destTextCtrl = useTextEditingController();
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
model.setUpController(srcTextCtrl, destTextCtrl);
|
||||||
|
return null;
|
||||||
|
}, const []);
|
||||||
|
|
||||||
return ContentDialog(
|
return ContentDialog(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: MediaQuery.of(context).size.width * .8,
|
maxWidth: MediaQuery.of(context).size.width * .8,
|
||||||
@ -67,7 +77,36 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
|||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 32),
|
SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text("远程输入服务:"),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
if (serverState.isServerStartup)
|
||||||
|
Button(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
ServerQrDialogUI(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
serverState.serverAddressText ?? "...",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 14),
|
||||||
|
ToggleSwitch(
|
||||||
|
checked: serverState.isServerStartup,
|
||||||
|
onChanged: (b) =>
|
||||||
|
_onSwitchServer(context, b, serverModel)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -130,4 +169,27 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onSwitchServer(
|
||||||
|
BuildContext context, bool value, InputMethodServer serverModel) async {
|
||||||
|
if (value) {
|
||||||
|
final userOK = await showConfirmDialogs(
|
||||||
|
context,
|
||||||
|
"确认启用远程输入?",
|
||||||
|
Text(
|
||||||
|
"开启此功能后,可通过手机访问远程服务地址,快捷输入文字,省去切换窗口的麻烦,游戏流程不中断。\n\n若弹出防火墙提示,请展开弹窗,手动勾选所有网络类型并允许,否则可能无法正常访问此功能。"),
|
||||||
|
);
|
||||||
|
if (userOK) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
await serverModel.startServer().unwrap(context: context);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => ServerQrDialogUI(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await serverModel.stopServer().unwrap(context: context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
|||||||
state = state.copyWith(enableAutoCopy: value);
|
state = state.copyWith(enableAutoCopy: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? onTextChange(String type, String str) {
|
String? onTextChange(String type, String str, {formWeb = false}) {
|
||||||
if (state.keyMaps == null || state.worldMaps == null) return null;
|
if (state.keyMaps == null || state.worldMaps == null) return null;
|
||||||
StringBuffer sb = StringBuffer();
|
StringBuffer sb = StringBuffer();
|
||||||
final r = RegExp(r'^[a-zA-Z0-9\p{P}\p{S}]+$');
|
final r = RegExp(r'^[a-zA-Z0-9\p{P}\p{S}]+$');
|
||||||
@ -93,7 +93,9 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
final text = "[zh] ${sb.toString()}";
|
final text = "[zh] ${sb.toString()}";
|
||||||
_handleAutoCopy(text);
|
if (!formWeb) {
|
||||||
|
_handleAutoCopy(text);
|
||||||
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,4 +116,26 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextEditingController? _srcTextCtrl;
|
||||||
|
TextEditingController? _destTextCtrl;
|
||||||
|
|
||||||
|
void setUpController(
|
||||||
|
TextEditingController srcTextCtrl, TextEditingController destTextCtrl) {
|
||||||
|
_srcTextCtrl = srcTextCtrl;
|
||||||
|
_destTextCtrl = destTextCtrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onSendText(
|
||||||
|
String text, {
|
||||||
|
bool autoCopy = false,
|
||||||
|
bool autoInput = false,
|
||||||
|
}) async {
|
||||||
|
debugPrint("[InputMethodDialogUIState] onSendText: $text");
|
||||||
|
_srcTextCtrl?.text = text;
|
||||||
|
_destTextCtrl?.text = onTextChange("src", text) ?? "";
|
||||||
|
if (_destTextCtrl?.text.isEmpty ?? true) return;
|
||||||
|
if (autoCopy) {
|
||||||
|
Clipboard.setData(ClipboardData(text: _destTextCtrl?.text ?? ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ part of 'input_method_dialog_ui_model.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$inputMethodDialogUIModelHash() =>
|
String _$inputMethodDialogUIModelHash() =>
|
||||||
r'48955b06db0b5fdc8ae5e59b93fdd9a95b391487';
|
r'93440d8f9c5372d5350ceaa8cb00a1b0d3b0046e';
|
||||||
|
|
||||||
/// See also [InputMethodDialogUIModel].
|
/// See also [InputMethodDialogUIModel].
|
||||||
@ProviderFor(InputMethodDialogUIModel)
|
@ProviderFor(InputMethodDialogUIModel)
|
||||||
|
212
lib/ui/home/input_method/server.dart
Normal file
212
lib/ui/home/input_method/server.dart
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:shelf/shelf_io.dart' as shelf_io;
|
||||||
|
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
|
||||||
|
|
||||||
|
import 'input_method_dialog_ui_model.dart';
|
||||||
|
|
||||||
|
part 'server.g.dart';
|
||||||
|
|
||||||
|
part 'server.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InputMethodServerState with _$InputMethodServerState {
|
||||||
|
const factory InputMethodServerState({
|
||||||
|
@Default(false) bool isServerStartup,
|
||||||
|
String? serverAddressText,
|
||||||
|
}) = _InputMethodServerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class InputMethodServer extends _$InputMethodServer {
|
||||||
|
@override
|
||||||
|
InputMethodServerState build() {
|
||||||
|
state = InputMethodServerState(isServerStartup: false);
|
||||||
|
ref.onDispose(() {
|
||||||
|
stopServer();
|
||||||
|
});
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizationUIState get _localizationUIState =>
|
||||||
|
ref.read(localizationUIModelProvider);
|
||||||
|
|
||||||
|
InputMethodDialogUIModel get _inputMethodDialogUIModel =>
|
||||||
|
ref.read(inputMethodDialogUIModelProvider.notifier);
|
||||||
|
|
||||||
|
HttpServer? _httpServer;
|
||||||
|
|
||||||
|
Future<void> stopServer() async {
|
||||||
|
if (_httpServer != null) {
|
||||||
|
await _httpServer!.close(force: true);
|
||||||
|
_httpServer = null;
|
||||||
|
state = state.copyWith(
|
||||||
|
isServerStartup: false,
|
||||||
|
);
|
||||||
|
dPrint("[InputMethodServer] stopServer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> startServer() async {
|
||||||
|
dPrint("[InputMethodServer] startServer");
|
||||||
|
|
||||||
|
var handler =
|
||||||
|
const Pipeline().addMiddleware(logRequests()).addHandler(_onHandler);
|
||||||
|
|
||||||
|
var server = await shelf_io.serve(
|
||||||
|
handler, "0.0.0.0", ConstConf.inputMethodServerPort);
|
||||||
|
|
||||||
|
// Enable content compression
|
||||||
|
server.autoCompress = true;
|
||||||
|
|
||||||
|
dPrint('Serving at http://${server.address.host}:${server.port}');
|
||||||
|
|
||||||
|
server.autoCompress = true;
|
||||||
|
_httpServer = server;
|
||||||
|
final address = await _findAddress();
|
||||||
|
state = state.copyWith(
|
||||||
|
isServerStartup: true,
|
||||||
|
serverAddressText: address,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _findAddress() async {
|
||||||
|
final list = <String>[];
|
||||||
|
final List<NetworkInterface> address = await NetworkInterface.list();
|
||||||
|
bool has192168 = false;
|
||||||
|
for (var value in address) {
|
||||||
|
for (var addr in value.addresses) {
|
||||||
|
if (addr.type == InternetAddressType.IPv4) {
|
||||||
|
list.add("http://${addr.address}:${ConstConf.inputMethodServerPort}");
|
||||||
|
if (addr.address.startsWith('192.168.')) {
|
||||||
|
has192168 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has192168) {
|
||||||
|
list.removeWhere((element) => !element.contains('192.168.'));
|
||||||
|
}
|
||||||
|
if (list.isEmpty) {
|
||||||
|
list.add("获取地址失败,请手动查看电脑IP");
|
||||||
|
}
|
||||||
|
return list.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> _onHandler(Request request) async {
|
||||||
|
final path = request.url.path;
|
||||||
|
dPrint("[InputMethodServer] path: $path");
|
||||||
|
Uint8List? contentByte;
|
||||||
|
String mimeType;
|
||||||
|
try {
|
||||||
|
if (path.startsWith('api')) {
|
||||||
|
return _onHandlerApi(request);
|
||||||
|
}
|
||||||
|
if (path == '/' || path == '') {
|
||||||
|
contentByte =
|
||||||
|
(await rootBundle.load('assets/web/input_method/index.html'))
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
|
mimeType = 'text/html; charset=utf-8';
|
||||||
|
} else {
|
||||||
|
var dotOffset = path.lastIndexOf('.');
|
||||||
|
if (path.substring(dotOffset) == '.png' ||
|
||||||
|
path.substring(dotOffset) == '.ttf' ||
|
||||||
|
path.substring(dotOffset) == '.otf') {
|
||||||
|
contentByte = (await rootBundle.load('assets/web/input_method/$path'))
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
|
} else {
|
||||||
|
contentByte = (await rootBundle.load('assets/web/input_method/$path'))
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeType = dotOffset == -1
|
||||||
|
? 'text/plain; charset=utf-8'
|
||||||
|
: {
|
||||||
|
'.html': 'text/html; charset=utf-8',
|
||||||
|
'.css': 'text/css; charset=utf-8',
|
||||||
|
'.js': 'text/javascript; charset=utf-8',
|
||||||
|
'.csv': 'text/csv; charset=utf-8',
|
||||||
|
'.txt': 'text/plain; charset=utf-8',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.jpg': 'image/jpg',
|
||||||
|
'.jpeg': 'image/jpeg',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.json': 'application/json',
|
||||||
|
'.xml': 'application/xml',
|
||||||
|
'.ttf': 'font/ttf',
|
||||||
|
'.otf': 'font/otf'
|
||||||
|
}[path.substring(dotOffset)] ??
|
||||||
|
"application/octet-stream";
|
||||||
|
}
|
||||||
|
return Response.ok(
|
||||||
|
contentByte,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': mimeType,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint(e.toString());
|
||||||
|
return Response.internalServerError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> _onHandlerApi(Request request) async {
|
||||||
|
final path = request.url.path;
|
||||||
|
if (path == "api") {
|
||||||
|
return Response.ok(json.encode({
|
||||||
|
"status": "ok",
|
||||||
|
"appVersion": ConstConf.appVersion,
|
||||||
|
"appVersionCode": ConstConf.appVersionCode,
|
||||||
|
"appVersionDate": ConstConf.appVersionDate,
|
||||||
|
"isMSE": ConstConf.isMSE,
|
||||||
|
"installedCommunityInputMethodSupportVersion":
|
||||||
|
_localizationUIState.installedCommunityInputMethodSupportVersion,
|
||||||
|
}));
|
||||||
|
} else if (path.startsWith("api/send") && request.method == "POST") {
|
||||||
|
final body = await request.readAsString();
|
||||||
|
final data = json.decode(body);
|
||||||
|
final text = data["text"] ?? "";
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return Response.badRequest(
|
||||||
|
body: json.encode({
|
||||||
|
"result": "error",
|
||||||
|
"message": "文本不能为空!",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
final autoCopy = data["autoCopy"] ?? false;
|
||||||
|
final autoInput = data["autoInput"] ?? false;
|
||||||
|
try {
|
||||||
|
await _inputMethodDialogUIModel.onSendText(
|
||||||
|
text,
|
||||||
|
autoCopy: autoCopy,
|
||||||
|
autoInput: autoInput,
|
||||||
|
);
|
||||||
|
return Response.ok(json.encode({
|
||||||
|
"result": "ok",
|
||||||
|
"message": "发送成功!",
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
return Response.internalServerError(
|
||||||
|
body: json.encode({
|
||||||
|
"result": "error",
|
||||||
|
"message": e.toString(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Response.notFound("Not Found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
lib/ui/home/input_method/server.freezed.dart
Normal file
171
lib/ui/home/input_method/server.freezed.dart
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// 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 'server.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 _$InputMethodServerState {
|
||||||
|
bool get isServerStartup => throw _privateConstructorUsedError;
|
||||||
|
String? get serverAddressText => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of InputMethodServerState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$InputMethodServerStateCopyWith<InputMethodServerState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $InputMethodServerStateCopyWith<$Res> {
|
||||||
|
factory $InputMethodServerStateCopyWith(InputMethodServerState value,
|
||||||
|
$Res Function(InputMethodServerState) then) =
|
||||||
|
_$InputMethodServerStateCopyWithImpl<$Res, InputMethodServerState>;
|
||||||
|
@useResult
|
||||||
|
$Res call({bool isServerStartup, String? serverAddressText});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$InputMethodServerStateCopyWithImpl<$Res,
|
||||||
|
$Val extends InputMethodServerState>
|
||||||
|
implements $InputMethodServerStateCopyWith<$Res> {
|
||||||
|
_$InputMethodServerStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of InputMethodServerState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? isServerStartup = null,
|
||||||
|
Object? serverAddressText = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
isServerStartup: null == isServerStartup
|
||||||
|
? _value.isServerStartup
|
||||||
|
: isServerStartup // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
serverAddressText: freezed == serverAddressText
|
||||||
|
? _value.serverAddressText
|
||||||
|
: serverAddressText // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$InputMethodServerStateImplCopyWith<$Res>
|
||||||
|
implements $InputMethodServerStateCopyWith<$Res> {
|
||||||
|
factory _$$InputMethodServerStateImplCopyWith(
|
||||||
|
_$InputMethodServerStateImpl value,
|
||||||
|
$Res Function(_$InputMethodServerStateImpl) then) =
|
||||||
|
__$$InputMethodServerStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({bool isServerStartup, String? serverAddressText});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$InputMethodServerStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$InputMethodServerStateCopyWithImpl<$Res,
|
||||||
|
_$InputMethodServerStateImpl>
|
||||||
|
implements _$$InputMethodServerStateImplCopyWith<$Res> {
|
||||||
|
__$$InputMethodServerStateImplCopyWithImpl(
|
||||||
|
_$InputMethodServerStateImpl _value,
|
||||||
|
$Res Function(_$InputMethodServerStateImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of InputMethodServerState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? isServerStartup = null,
|
||||||
|
Object? serverAddressText = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$InputMethodServerStateImpl(
|
||||||
|
isServerStartup: null == isServerStartup
|
||||||
|
? _value.isServerStartup
|
||||||
|
: isServerStartup // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
serverAddressText: freezed == serverAddressText
|
||||||
|
? _value.serverAddressText
|
||||||
|
: serverAddressText // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$InputMethodServerStateImpl implements _InputMethodServerState {
|
||||||
|
const _$InputMethodServerStateImpl(
|
||||||
|
{this.isServerStartup = false, this.serverAddressText});
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isServerStartup;
|
||||||
|
@override
|
||||||
|
final String? serverAddressText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'InputMethodServerState(isServerStartup: $isServerStartup, serverAddressText: $serverAddressText)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$InputMethodServerStateImpl &&
|
||||||
|
(identical(other.isServerStartup, isServerStartup) ||
|
||||||
|
other.isServerStartup == isServerStartup) &&
|
||||||
|
(identical(other.serverAddressText, serverAddressText) ||
|
||||||
|
other.serverAddressText == serverAddressText));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, isServerStartup, serverAddressText);
|
||||||
|
|
||||||
|
/// Create a copy of InputMethodServerState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$InputMethodServerStateImplCopyWith<_$InputMethodServerStateImpl>
|
||||||
|
get copyWith => __$$InputMethodServerStateImplCopyWithImpl<
|
||||||
|
_$InputMethodServerStateImpl>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _InputMethodServerState implements InputMethodServerState {
|
||||||
|
const factory _InputMethodServerState(
|
||||||
|
{final bool isServerStartup,
|
||||||
|
final String? serverAddressText}) = _$InputMethodServerStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isServerStartup;
|
||||||
|
@override
|
||||||
|
String? get serverAddressText;
|
||||||
|
|
||||||
|
/// Create a copy of InputMethodServerState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$InputMethodServerStateImplCopyWith<_$InputMethodServerStateImpl>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
26
lib/ui/home/input_method/server.g.dart
Normal file
26
lib/ui/home/input_method/server.g.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'server.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$inputMethodServerHash() => r'4ea07de4bca3268933b78335b670c09e6fac61bc';
|
||||||
|
|
||||||
|
/// See also [InputMethodServer].
|
||||||
|
@ProviderFor(InputMethodServer)
|
||||||
|
final inputMethodServerProvider = AutoDisposeNotifierProvider<InputMethodServer,
|
||||||
|
InputMethodServerState>.internal(
|
||||||
|
InputMethodServer.new,
|
||||||
|
name: r'inputMethodServerProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$inputMethodServerHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$InputMethodServer = AutoDisposeNotifier<InputMethodServerState>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
95
lib/ui/home/input_method/server_qr_dialog_ui.dart
Normal file
95
lib/ui/home/input_method/server_qr_dialog_ui.dart
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
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:qr_flutter/qr_flutter.dart';
|
||||||
|
|
||||||
|
import 'server.dart';
|
||||||
|
|
||||||
|
class ServerQrDialogUI extends HookConsumerWidget {
|
||||||
|
const ServerQrDialogUI({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final serverState = ref.watch(inputMethodServerProvider);
|
||||||
|
|
||||||
|
final urls = serverState.serverAddressText?.split(",") ?? [""];
|
||||||
|
|
||||||
|
final hasMultipleUrls = urls.length > 1;
|
||||||
|
|
||||||
|
final index = useState(0);
|
||||||
|
|
||||||
|
return ContentDialog(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width * .4,
|
||||||
|
),
|
||||||
|
title: makeTitle(context),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: double.infinity, height: 12),
|
||||||
|
if (hasMultipleUrls) ...[
|
||||||
|
Text("我们没能找到合适的 ip 地址来访问服务,请您尝试以下地址(左右切换)"),
|
||||||
|
] else
|
||||||
|
Text(
|
||||||
|
"请使用您的移动设备扫描以下二维码,或手动访问连接",
|
||||||
|
style: TextStyle(color: Colors.white.withOpacity(.8)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (hasMultipleUrls)
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(FluentIcons.chevron_left),
|
||||||
|
onPressed: () {
|
||||||
|
index.value = (index.value - 1) % urls.length;
|
||||||
|
}),
|
||||||
|
SizedBox(width: 24),
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: QrImageView(
|
||||||
|
data: urls[index.value],
|
||||||
|
size: 200,
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 24),
|
||||||
|
if (hasMultipleUrls)
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(FluentIcons.chevron_right),
|
||||||
|
onPressed: () {
|
||||||
|
index.value = (index.value + 1) % urls.length;
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
hasMultipleUrls
|
||||||
|
? "(${index.value + 1} / ${urls.length})"
|
||||||
|
: urls[index.value],
|
||||||
|
style: TextStyle(fontSize: 13, color: Colors.white.withOpacity(.6)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeTitle(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
FluentIcons.back,
|
||||||
|
size: 22,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
context.pop();
|
||||||
|
}),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text("服务二维码"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/src/widgets/framework.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -12,7 +11,6 @@ import 'package:re_highlight/languages/ini.dart';
|
|||||||
import 'package:re_highlight/styles/vs2015.dart';
|
import 'package:re_highlight/styles/vs2015.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
|
||||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||||
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
||||||
|
@ -212,7 +212,6 @@ class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res>
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$AdvancedLocalizationUIStateImpl
|
class _$AdvancedLocalizationUIStateImpl
|
||||||
with DiagnosticableTreeMixin
|
|
||||||
implements _AdvancedLocalizationUIState {
|
implements _AdvancedLocalizationUIState {
|
||||||
_$AdvancedLocalizationUIStateImpl(
|
_$AdvancedLocalizationUIStateImpl(
|
||||||
{this.workingText = "",
|
{this.workingText = "",
|
||||||
@ -258,26 +257,10 @@ class _$AdvancedLocalizationUIStateImpl
|
|||||||
final String errorMessage;
|
final String errorMessage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString() {
|
||||||
return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni, customizeGlobalIni: $customizeGlobalIni, apiLocalizationData: $apiLocalizationData, p4kGlobalIniLines: $p4kGlobalIniLines, serverGlobalIniLines: $serverGlobalIniLines, errorMessage: $errorMessage)';
|
return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni, customizeGlobalIni: $customizeGlobalIni, apiLocalizationData: $apiLocalizationData, p4kGlobalIniLines: $p4kGlobalIniLines, serverGlobalIniLines: $serverGlobalIniLines, errorMessage: $errorMessage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
||||||
super.debugFillProperties(properties);
|
|
||||||
properties
|
|
||||||
..add(DiagnosticsProperty('type', 'AdvancedLocalizationUIState'))
|
|
||||||
..add(DiagnosticsProperty('workingText', workingText))
|
|
||||||
..add(DiagnosticsProperty('classMap', classMap))
|
|
||||||
..add(DiagnosticsProperty('p4kGlobalIni', p4kGlobalIni))
|
|
||||||
..add(DiagnosticsProperty('serverGlobalIni', serverGlobalIni))
|
|
||||||
..add(DiagnosticsProperty('customizeGlobalIni', customizeGlobalIni))
|
|
||||||
..add(DiagnosticsProperty('apiLocalizationData', apiLocalizationData))
|
|
||||||
..add(DiagnosticsProperty('p4kGlobalIniLines', p4kGlobalIniLines))
|
|
||||||
..add(DiagnosticsProperty('serverGlobalIniLines', serverGlobalIniLines))
|
|
||||||
..add(DiagnosticsProperty('errorMessage', errorMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
|
@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$advancedLocalizationUIModelHash() =>
|
String _$advancedLocalizationUIModelHash() =>
|
||||||
r'8241143c6dec93cd705e6b2e65cbca711cdfe2fb';
|
r'6b65988e71733c01e9352765cf600c4781bdccb4';
|
||||||
|
|
||||||
/// See also [AdvancedLocalizationUIModel].
|
/// See also [AdvancedLocalizationUIModel].
|
||||||
@ProviderFor(AdvancedLocalizationUIModel)
|
@ProviderFor(AdvancedLocalizationUIModel)
|
||||||
|
@ -4,7 +4,6 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
|||||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
|
||||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||||
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
|
@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$localizationUIModelHash() =>
|
String _$localizationUIModelHash() =>
|
||||||
r'b8c893413fa8a314d0fa3b2cfffb63f723226bae';
|
r'206512f457acdb0aaa2cd638fcdb31b6a88848a6';
|
||||||
|
|
||||||
/// See also [LocalizationUIModel].
|
/// See also [LocalizationUIModel].
|
||||||
@ProviderFor(LocalizationUIModel)
|
@ProviderFor(LocalizationUIModel)
|
||||||
|
@ -63,6 +63,8 @@ dependencies:
|
|||||||
file: ^7.0.0
|
file: ^7.0.0
|
||||||
re_editor: ^0.6.0
|
re_editor: ^0.6.0
|
||||||
re_highlight: ^0.0.3
|
re_highlight: ^0.0.3
|
||||||
|
shelf: ^1.4.1
|
||||||
|
qr_flutter: ^4.1.0
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
http: ^1.1.2
|
http: ^1.1.2
|
||||||
|
|
||||||
@ -89,6 +91,9 @@ flutter:
|
|||||||
- assets/
|
- assets/
|
||||||
- assets/binary/
|
- assets/binary/
|
||||||
- assets/countdown/
|
- assets/countdown/
|
||||||
|
- assets/web/input_method/
|
||||||
|
- assets/web/input_method/js/
|
||||||
|
- assets/web/input_method/style/
|
||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
- family: SourceHanSansCN-Regular
|
- family: SourceHanSansCN-Regular
|
||||||
|
Loading…
Reference in New Issue
Block a user