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.",
|
"mpv": "Fetch page data from Multi-Page Viewer.",
|
||||||
"downloadOriginalImg": "Download original images.",
|
"downloadOriginalImg": "Download original images.",
|
||||||
"exportZipJpnTitle": "Use japanese title first when exporting zip.",
|
"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 获取页面数据。",
|
"mpv": "从 Multi-Page Viewer 获取页面数据。",
|
||||||
"downloadOriginalImg": "下载原始画质的图片。",
|
"downloadOriginalImg": "下载原始画质的图片。",
|
||||||
"exportZipJpnTitle": "导出Zip时优先使用日语标题。",
|
"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 isSafari => false;
|
||||||
bool get isMobile => false;
|
bool get isMobile => false;
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ import 'package:user_agent_analyzer/user_agent_analyzer.dart';
|
|||||||
UserAgent? _ua;
|
UserAgent? _ua;
|
||||||
|
|
||||||
UserAgent get ua => _ua ??= UserAgent(window.navigator.userAgent);
|
UserAgent get ua => _ua ??= UserAgent(window.navigator.userAgent);
|
||||||
|
String get oUA => window.navigator.userAgent;
|
||||||
bool get isSafari => ua.isSafari;
|
bool get isSafari => ua.isSafari;
|
||||||
bool get isMobile => ua.isMobile;
|
bool get isMobile => ua.isMobile;
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'api/config.dart';
|
import 'api/config.dart';
|
||||||
|
import 'components/number_field.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
|
import 'platform/ua.dart';
|
||||||
|
|
||||||
final _log = Logger("ServerSettingsPage");
|
final _log = Logger("ServerSettingsPage");
|
||||||
|
|
||||||
@@ -28,6 +31,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
|
|||||||
Object? _error;
|
Object? _error;
|
||||||
CancelToken? _cancel;
|
CancelToken? _cancel;
|
||||||
CancelToken? _saveCancel;
|
CancelToken? _saveCancel;
|
||||||
|
late TextEditingController _uaController;
|
||||||
|
|
||||||
Future<void> _fetchData() async {
|
Future<void> _fetchData() async {
|
||||||
_cancel = CancelToken();
|
_cancel = CancelToken();
|
||||||
@@ -83,6 +87,9 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
|
|||||||
_changed = false;
|
_changed = false;
|
||||||
_controller = ScrollController();
|
_controller = ScrollController();
|
||||||
_now = ConfigOptional();
|
_now = ConfigOptional();
|
||||||
|
if (kIsWeb) {
|
||||||
|
_uaController = TextEditingController();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -91,6 +98,9 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
|
|||||||
_formKey.currentState?.dispose();
|
_formKey.currentState?.dispose();
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
_saveCancel?.cancel();
|
_saveCancel?.cancel();
|
||||||
|
if (kIsWeb) {
|
||||||
|
_uaController.dispose();
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +174,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
|
|||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildListDelegate([
|
delegate: SliverChildListDelegate([
|
||||||
_buildCheckBox(context),
|
_buildCheckBox(context),
|
||||||
|
_buildTextBox(context),
|
||||||
_buildBottomBar(context),
|
_buildBottomBar(context),
|
||||||
])),
|
])),
|
||||||
],
|
],
|
||||||
@@ -174,7 +185,7 @@ class _ServerSettingsPage extends State<ServerSettingsPage>
|
|||||||
return Container(
|
return Container(
|
||||||
padding: MediaQuery.of(context).size.width > 810
|
padding: MediaQuery.of(context).size.width > 810
|
||||||
? const EdgeInsets.symmetric(horizontal: 100)
|
? const EdgeInsets.symmetric(horizontal: 100)
|
||||||
: null,
|
: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: child,
|
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) {
|
Widget _buildBottomBar(BuildContext context) {
|
||||||
final i18n = AppLocalizations.of(context)!;
|
final i18n = AppLocalizations.of(context)!;
|
||||||
return _buildWithHorizontalPadding(
|
return _buildWithHorizontalPadding(
|
||||||
|
|||||||
Reference in New Issue
Block a user