mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
update server settings page
This commit is contained in:
70
lib/components/number_field.dart
Normal file
70
lib/components/number_field.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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": "数字超出了范围。"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
String get oUA => "";
|
||||
bool get isSafari => false;
|
||||
bool get isMobile => false;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user