update server settings page

This commit is contained in:
2024-01-19 15:42:27 +08:00
parent a038421269
commit cd0c49b86e
6 changed files with 195 additions and 3 deletions

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class NumberFormField extends StatelessWidget {
const NumberFormField(
{Key? key,
this.min,
this.max,
this.initialValue,
this.errorMsg,
this.decoration,
this.onChanged})
: super(key: key);
final int? min;
final int? max;
final int? initialValue;
final String? errorMsg;
final InputDecoration? decoration;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
return TextFormField(
initialValue: initialValue?.toString(),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r"[\-0-9]")),
],
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (String? value) {
if (value == null || value.isEmpty) {
return null;
}
try {
int v = int.parse(value);
if (min != null && v < min!) {
return errorMsg ?? i18n.numberOutOfRange;
}
if (max != null && v > max!) {
return errorMsg ?? i18n.numberOutOfRange;
}
} catch (e) {
return errorMsg ?? i18n.invalidNumber;
}
return null;
},
decoration: decoration,
onChanged: (String? value) {
if (value == null || value.isEmpty) {
onChanged?.call(null);
} else {
try {
int v = int.parse(value);
if (max != null && v > max!) {
onChanged?.call(null);
} else if (min != null && v < min!) {
onChanged?.call(null);
} else {
onChanged?.call(v);
}
} catch (e) {
onChanged?.call(null);
}
}
});
}
}

View File

@@ -97,5 +97,15 @@
"mpv": "Fetch page data from Multi-Page Viewer.",
"downloadOriginalImg": "Download original images.",
"exportZipJpnTitle": "Use japanese title first when exporting zip.",
"removePreviousGallery": "Remove old galleries which replaced by new ones."
"removePreviousGallery": "Remove old galleries which replaced by new ones.",
"serverDbPath": "The folder where the database is stored",
"serverDbPathHelp": "If not set, the download location is used.",
"userAgent": "User Agent",
"useBrowserUA": "Use current browser's user agent",
"downloadLocation": "Download location",
"enterCookies": "Enter cookies here",
"enterNewCookies": "Enter new cookies here",
"maxTaskCount": "Maximum number of parallel tasks",
"invalidNumber": "Invalid number.",
"numberOutOfRange": "The number is out of range."
}

View File

@@ -97,5 +97,15 @@
"mpv": "从 Multi-Page Viewer 获取页面数据。",
"downloadOriginalImg": "下载原始画质的图片。",
"exportZipJpnTitle": "导出Zip时优先使用日语标题。",
"removePreviousGallery": "移除被新画廊替代的旧画廊。"
"removePreviousGallery": "移除被新画廊替代的旧画廊。",
"serverDbPath": "存放数据库的文件夹位置",
"serverDbPathHelp": "如果未设置,将使用下载位置。",
"userAgent": "用户代理(User Agent)",
"useBrowserUA": "使用当前浏览器的用户代理",
"downloadLocation": "下载位置",
"enterCookies": "在此输入 Cookies",
"enterNewCookies": "在此输入新的 Cookies",
"maxTaskCount": "最大并行任务数",
"invalidNumber": "非法的数字。",
"numberOutOfRange": "数字超出了范围。"
}

View File

@@ -1,2 +1,3 @@
String get oUA => "";
bool get isSafari => false;
bool get isMobile => false;

View File

@@ -5,5 +5,6 @@ import 'package:user_agent_analyzer/user_agent_analyzer.dart';
UserAgent? _ua;
UserAgent get ua => _ua ??= UserAgent(window.navigator.userAgent);
String get oUA => window.navigator.userAgent;
bool get isSafari => ua.isSafari;
bool get isMobile => ua.isMobile;

View File

@@ -1,10 +1,13 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';
import 'api/config.dart';
import 'components/number_field.dart';
import 'globals.dart';
import 'platform/ua.dart';
final _log = Logger("ServerSettingsPage");
@@ -28,6 +31,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
Object? _error;
CancelToken? _cancel;
CancelToken? _saveCancel;
late TextEditingController _uaController;
Future<void> _fetchData() async {
_cancel = CancelToken();
@@ -83,6 +87,9 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
_changed = false;
_controller = ScrollController();
_now = ConfigOptional();
if (kIsWeb) {
_uaController = TextEditingController();
}
}
@override
@@ -91,6 +98,9 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
_formKey.currentState?.dispose();
_controller.dispose();
_saveCancel?.cancel();
if (kIsWeb) {
_uaController.dispose();
}
super.dispose();
}
@@ -164,6 +174,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
SliverList(
delegate: SliverChildListDelegate([
_buildCheckBox(context),
_buildTextBox(context),
_buildBottomBar(context),
])),
],
@@ -174,7 +185,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
return Container(
padding: MediaQuery.of(context).size.width > 810
? const EdgeInsets.symmetric(horizontal: 100)
: null,
: const EdgeInsets.symmetric(horizontal: 16),
child: child,
);
}
@@ -250,6 +261,95 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
]));
}
Widget _buildTextBox(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
if (kIsWeb) {
_uaController.text = _now.ua ?? _config!.ua ?? "";
}
return _buildWithHorizontalPadding(
context,
Column(mainAxisSize: MainAxisSize.min, children: [
_buildWithVecticalPadding(TextFormField(
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText:
_config!.cookies ? i18n.enterNewCookies : i18n.enterCookies,
),
onChanged: (s) {
setState(() {
_now.cookies = s;
_changed = true;
});
},
)),
_buildWithVecticalPadding(TextFormField(
initialValue: _now.dbPath ?? _config!.dbPath,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: i18n.serverDbPath,
helperText: i18n.serverDbPathHelp,
),
onChanged: (s) {
setState(() {
_now.dbPath = s;
_changed = true;
});
})),
_buildWithVecticalPadding(TextFormField(
initialValue: kIsWeb ? null : _now.ua ?? _config!.ua,
controller: kIsWeb ? _uaController : null,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: i18n.userAgent,
counter: kIsWeb
? TextButton(
onPressed: () {
setState(() {
_now.ua = oUA;
_changed = true;
});
},
child: Text(i18n.useBrowserUA))
: null,
),
onChanged: (s) {
setState(() {
_now.ua = s;
_changed = true;
});
})),
_buildWithVecticalPadding(TextFormField(
initialValue: _now.base ?? _config!.base,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: i18n.downloadLocation,
),
onChanged: (s) {
setState(() {
_now.base = s;
_changed = true;
});
},
)),
_buildWithVecticalPadding(NumberFormField(
min: 1,
initialValue: _now.maxTaskCount ?? _config!.maxTaskCount,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: i18n.maxTaskCount,
),
onChanged: (s) {
if (s != null) {
setState(() {
_now.maxTaskCount = s;
_changed = true;
});
}
},
)),
]));
}
Widget _buildBottomBar(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
return _buildWithHorizontalPadding(