diff --git a/lib/auth.dart b/lib/auth.dart index 6254505..35a10a9 100644 --- a/lib/auth.dart +++ b/lib/auth.dart @@ -88,6 +88,7 @@ class AuthInfo { final u = _user!; _log.info( "Logged in as ${u.username} (${u.id}). isAdmin: ${u.isAdmin}. permissions: ${u.permissions}"); + listener.tryEmit("user_logined", null); await checkSessionInfo(); if (canManageTasks == true) { if (!tasks.inited) tasks.init(); diff --git a/lib/globals.dart b/lib/globals.dart index c72dc05..ce07adb 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -100,7 +100,7 @@ bool tryInitApi(BuildContext context) { String? baseUrl = prefs.getString("baseUrl"); if (baseUrl == null) { SchedulerBinding.instance.addPostFrameCallback((_) { - context.go("/set_server"); + context.go("/settings/server/url"); }); return false; } @@ -138,20 +138,15 @@ final GlobalKey rootScaffoldMessengerKey = final EventListener listener = EventListener()..maxListeners = 0; enum MoreVertSettings { - setServerUrl, createRootUser, settings, markAsNsfw, markAsSfw, - serverSettings, taskManager, } void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) { switch (value) { - case MoreVertSettings.setServerUrl: - context.push("/set_server"); - break; case MoreVertSettings.createRootUser: context.push("/create_root_user"); break; @@ -164,9 +159,6 @@ void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) { case MoreVertSettings.markAsSfw: GalleryPage.maybeOf(context)?.markGalleryAsNsfw(false); break; - case MoreVertSettings.serverSettings: - context.push("/server_settings"); - break; case MoreVertSettings.taskManager: context.push("/task_manager"); break; @@ -179,12 +171,6 @@ List> buildMoreVertSettings( BuildContext context) { var list = >[]; var path = GoRouterState.of(context).path; - if (const bool.fromEnvironment("skipBaseUrl") != true && - path != "/set_server") { - list.add(PopupMenuItem( - value: MoreVertSettings.setServerUrl, - child: Text(AppLocalizations.of(context)!.setServerUrl))); - } if (auth.status != null && auth.status!.noUser && prefs.getBool("skipCreateRootUser") == true && @@ -198,11 +184,6 @@ List> buildMoreVertSettings( value: MoreVertSettings.settings, child: Text(AppLocalizations.of(context)!.settings))); } - if (path != "/server_settings" && auth.isAdmin == true) { - list.add(PopupMenuItem( - value: MoreVertSettings.serverSettings, - child: Text(AppLocalizations.of(context)!.serverSettings))); - } if (path != "/task_manager" && auth.canManageTasks == true) { list.add(PopupMenuItem( value: MoreVertSettings.taskManager, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7ca4f03..addc390 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -11,7 +11,7 @@ "incorrectUserPassword": "Incorrect username or password.", "networkError": "Network error.", "internalError": "Some internal error occurred. Please send the log file to the developer.", - "setServerUrl": "Server URL Settings", + "setServerUrl": "Server URL", "createRootUser": "Create Root User", "skip": "Skip", "create": "Create", @@ -92,7 +92,7 @@ "markAsSfw": "Mark as SFW", "markAsAd": "Mark as Ad", "markAsNonAd": "Mark as non-Ad", - "serverSettings": "Server Settings", + "server": "Server", "useEx": "Use exhentai.org.", "mpv": "Fetch page data from Multi-Page Viewer.", "downloadOriginalImg": "Download original images.", @@ -225,5 +225,7 @@ } }, "failedDeleteUser": "Failed to delete user: ", - "deleteUser": "Delete user" + "deleteUser": "Delete user", + "display": "Display", + "cache": "Cache" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 6143e3d..d5affed 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -11,7 +11,7 @@ "incorrectUserPassword": "不正确的用户名或密码。", "networkError": "网络错误。", "internalError": "出现了内部错误,请将日志发送给开发者。", - "setServerUrl": "服务器地址设置", + "setServerUrl": "服务器地址", "createRootUser": "创建根用户", "skip": "跳过", "create": "创建", @@ -92,7 +92,7 @@ "markAsSfw": "标记为SFW", "markAsAd": "标记为广告", "markAsNonAd": "标记为非广告", - "serverSettings": "服务器设置", + "server": "服务器", "useEx": "使用 exhentai.org。", "mpv": "从 Multi-Page Viewer 获取页面数据。", "downloadOriginalImg": "下载原始画质的图片。", @@ -225,5 +225,7 @@ } }, "failedDeleteUser": "删除用户失败:", - "deleteUser": "删除用户" + "deleteUser": "删除用户", + "display": "显示", + "cache": "缓存" } diff --git a/lib/main.dart b/lib/main.dart index b500a3a..3cf2319 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,9 +21,11 @@ import 'pages/galleries.dart'; import 'pages/gallery.dart'; import 'pages/home.dart'; import 'pages/login.dart'; -import 'pages/server_settings.dart'; -import 'pages/set_server.dart'; import 'pages/settings.dart'; +import 'pages/settings/cache.dart'; +import 'pages/settings/display.dart'; +import 'pages/settings/server.dart'; +import 'pages/settings/server_url.dart'; import 'pages/task_manager.dart'; import 'pages/users.dart'; import 'utils.dart'; @@ -38,8 +40,8 @@ final _router = GoRouter( builder: (context, state) => const HomePage(), ), GoRoute( - path: SetServerPage.routeName, - builder: (context, state) => const SetServerPage(), + path: ServerUrlSettingsPage.routeName, + builder: (context, state) => ServerUrlSettingsPage(key: state.pageKey), ), GoRoute( path: LoginPage.routeName, @@ -51,7 +53,7 @@ final _router = GoRouter( ), GoRoute( path: SettingsPage.routeName, - builder: (context, state) => const SettingsPage(), + builder: (context, state) => SettingsPage(key: state.pageKey), ), GoRoute( name: GalleriesPage.routeName, @@ -239,6 +241,14 @@ final _router = GoRouter( return "/users"; } }), + GoRoute( + path: DisplaySettingsPage.routeName, + builder: (context, state) => DisplaySettingsPage(key: state.pageKey), + ), + GoRoute( + path: CacheSettingsPage.routeName, + builder: (context, state) => CacheSettingsPage(key: state.pageKey), + ), ], ); diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 682bdd5..f43c9c5 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -33,16 +33,6 @@ class HomeDrawer extends StatelessWidget { context.push("/galleries"); }, ), - auth.isAdmin == true - ? ListTile( - leading: const Icon(Icons.admin_panel_settings), - title: Text(i18n.serverSettings), - onTap: () { - Scaffold.of(context).closeDrawer(); - context.push("/server_settings"); - }, - ) - : Container(), auth.canManageTasks == true ? ListTile( leading: const Icon(Icons.task), diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 0660e66..67f6360 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,13 +1,7 @@ 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 '../globals.dart'; -import '../main.dart'; -import '../utils.dart'; -import '../utils/filesize.dart'; - -final _log = Logger("SettingsPage"); class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @@ -18,245 +12,35 @@ class SettingsPage extends StatefulWidget { State createState() => _SettingsPage(); } -class _SettingsPage extends State with ThemeModeWidget { - bool _oriDisplayAd = false; - Lang _oriLang = Lang.system; - bool _oriPreventScreenCapture = false; - bool _oriShowNsfw = false; - bool _oriShowTranslatedTag = false; - bool _oriUseTitleJpn = false; - bool _oriDlUseAvgSpeed = false; - bool _oriEnableImageCache = false; - bool _displayAd = false; - Lang _lang = Lang.system; - bool _preventScreenCapture = false; - bool _showNsfw = false; - bool _showTranslatedTag = false; - bool _useTitleJpn = false; - bool _dlUseAvgSpeed = false; - bool _enableImageCache = false; +class _SettingsPage extends State + with ThemeModeWidget, IsTopWidget2 { + void _onStateChanged(dynamic _) { + setState(() {}); + } + @override void initState() { + listener.on("user_logined", _onStateChanged); super.initState(); - try { - _oriLang = Lang.values[prefs.getInt("lang") ?? 0]; - _lang = _oriLang; - } catch (e) { - _log.warning("Failed to get lang:", e); - _oriLang = Lang.system; - _lang = Lang.system; - } - try { - _oriUseTitleJpn = prefs.getBool("useTitleJpn") ?? false; - _useTitleJpn = _oriUseTitleJpn; - } catch (e) { - _log.warning("Failed to get useTitleJpn:", e); - _oriUseTitleJpn = false; - _useTitleJpn = false; - } - try { - _oriShowNsfw = prefs.getBool("showNsfw") ?? false; - _showNsfw = _oriShowNsfw; - } catch (e) { - _log.warning("Failed to get showNsfw:", e); - _oriShowNsfw = false; - _showNsfw = false; - } - try { - _oriDisplayAd = prefs.getBool("displayAd") ?? false; - _displayAd = _oriDisplayAd; - } catch (e) { - _log.warning("Failed to get displayAd:", e); - _oriDisplayAd = false; - _displayAd = false; - } - try { - _oriShowTranslatedTag = prefs.getBool("showTranslatedTag") ?? - _oriLang.toLocale().languageCode == "zh"; - _showTranslatedTag = _oriShowTranslatedTag; - } catch (e) { - _log.warning("Failed to get showTranslatedTag:", e); - _oriShowTranslatedTag = false; - _showTranslatedTag = false; - } - try { - _oriPreventScreenCapture = prefs.getBool("preventScreenCapture") ?? false; - _preventScreenCapture = _oriPreventScreenCapture; - } catch (e) { - _log.warning("Failed to get preventScreenCapture:", e); - _oriPreventScreenCapture = false; - _preventScreenCapture = false; - } - try { - _oriDlUseAvgSpeed = prefs.getBool("dlUseAvgSpeed") ?? false; - _dlUseAvgSpeed = _oriDlUseAvgSpeed; - } catch (e) { - _log.warning("Failed to get dlUseAvgSpeed:", e); - _oriDlUseAvgSpeed = false; - _dlUseAvgSpeed = false; - } - try { - _oriEnableImageCache = prefs.getBool("enableImageCache") ?? true; - _enableImageCache = _oriEnableImageCache; - } catch (e) { - _log.warning("Failed to get enableImageCache:", e); - _oriEnableImageCache = true; - _enableImageCache = true; - } } - void fallback(BuildContext context) { - if (_oriLang != _lang) { - MainApp.of(context).changeLang(_oriLang); - } - } - - void reset(BuildContext context) { - if (_lang != Lang.system) MainApp.of(context).changeLang(Lang.system); - setState(() { - _lang = Lang.system; - _useTitleJpn = false; - _showNsfw = false; - _displayAd = false; - _showTranslatedTag = _oriLang.toLocale().languageCode == "zh"; - _preventScreenCapture = false; - _dlUseAvgSpeed = false; - _enableImageCache = true; - }); - } - - Future save() async { - bool re = true; - if (_lang != _oriLang && !await prefs.setInt("lang", _lang.index)) { - re = false; - _log.warning("Failed to save lang."); - } else { - _oriLang = _lang; - } - if (_oriUseTitleJpn != _useTitleJpn) { - if (!await prefs.setBool("useTitleJpn", _useTitleJpn)) { - re = false; - _log.warning("Failed to save useTitleJpn."); - } else { - _oriUseTitleJpn = _useTitleJpn; - } - } - if (_oriShowNsfw != _showNsfw) { - if (!await prefs.setBool("showNsfw", _showNsfw)) { - re = false; - _log.warning("Failed to save showNsfw."); - } else { - _oriShowNsfw = _showNsfw; - } - } - if (_oriDisplayAd != _displayAd) { - if (!await prefs.setBool("displayAd", _displayAd)) { - re = false; - _log.warning("Failed to save displayAd."); - } else { - _oriDisplayAd = _displayAd; - } - } - if (_oriShowTranslatedTag != _showTranslatedTag) { - if (!await prefs.setBool("showTranslatedTag", _showTranslatedTag)) { - re = false; - _log.warning("Failed to save showTranslatedTag."); - } else { - _oriShowTranslatedTag = _showTranslatedTag; - } - } - if (_oriPreventScreenCapture != _preventScreenCapture) { - if (!await prefs.setBool("preventScreenCapture", _preventScreenCapture)) { - re = false; - _log.warning("Failed to save preventScreenCapture."); - } else { - _oriPreventScreenCapture = _preventScreenCapture; - } - if (_preventScreenCapture) { - if (!await platformDisplay.enableProtect()) { - _log.warning("Failed to enable protect."); - } - } else { - if (!await platformDisplay.disableProtect()) { - _log.warning("Failed to disable protect."); - } - } - } - if (_dlUseAvgSpeed != _oriDlUseAvgSpeed) { - if (!await prefs.setBool("dlUseAvgSpeed", _dlUseAvgSpeed)) { - re = false; - _log.warning("Failed to save dlUseAvgSpeed."); - } else { - _oriDlUseAvgSpeed = _dlUseAvgSpeed; - } - } - if (_enableImageCache != _oriEnableImageCache) { - if (!await prefs.setBool("enableImageCache", _enableImageCache)) { - re = false; - _log.warning("Failed to save enableImageCache."); - } else { - _oriEnableImageCache = _enableImageCache; - } - } - return re; - } - - Widget _buildCache(BuildContext context) { - final i18n = AppLocalizations.of(context)!; - final text = SelectableText( - "${i18n.cachedFileSize}${i18n.colon}${getFileSize(imageCaches.size)}"); - final button = ElevatedButton( - onPressed: () { - imageCaches.updateSize(clear: true).then((_) { - setState(() {}); - }).onError((e, _) { - _log.warning("Failed to update image cache size: $e"); - return null; - }); - }, - child: Text(i18n.updateFileSize)); - final cButton = Container( - padding: const EdgeInsets.only(left: 8), - child: ElevatedButton( - onPressed: () { - imageCaches.clear().then((_) { - setState(() {}); - }).onError((e, _) { - _log.warning("Failed to clear image cache: $e"); - return null; - }); - }, - child: Text(i18n.clearCaches))); - final maxWidth = MediaQuery.of(context).size.width; - final useTwoLine = maxWidth <= 500 ? true : false; - return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - CheckboxMenuButton( - value: _enableImageCache, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _enableImageCache = value; - }); - } - }, - child: Text(i18n.enableImageCache), - ), - Container( - padding: const EdgeInsets.only(left: 6, top: 4), - child: useTwoLine ? text : Row(children: [text, button, cButton])), - useTwoLine ? Row(children: [button, cButton]) : Container(), - ]); + @override + void dispose() { + listener.removeEventListener("user_logined", _onStateChanged); + super.dispose(); } @override Widget build(BuildContext context) { + tryInitApi(context); final i18n = AppLocalizations.of(context)!; - setCurrentTitle(i18n.settings, Theme.of(context).primaryColor.value); + if (isTop(context)) { + setCurrentTitle(i18n.settings, Theme.of(context).primaryColor.value); + } return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () { - fallback(context); context.canPop() ? context.pop() : context.go("/"); }, icon: const Icon(Icons.arrow_back), @@ -270,154 +54,40 @@ class _SettingsPage extends State with ThemeModeWidget { body: LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( - child: ConstrainedBox( - constraints: - BoxConstraints(minHeight: constraints.maxHeight), - child: Container( - padding: MediaQuery.of(context).size.width > 810 - ? const EdgeInsets.symmetric(horizontal: 100) - : null, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: - const EdgeInsets.symmetric(vertical: 8), - child: DropdownMenu( - initialSelection: _lang, - onSelected: (value) { - if (value != null) { - setState(() { - _lang = value; - }); - MainApp.of(context).changeLang(_lang); - } - }, - label: Text(i18n.lang), - dropdownMenuEntries: Lang.values - .map((e) => DropdownMenuEntry( - value: e, - label: e == Lang.system - ? i18n.systemLang - : e.langName)) - .toList(), - leadingIcon: const Icon(Icons.language), - )), - Container( - padding: - const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _useTitleJpn, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _useTitleJpn = value; - }); - } - }, - child: Text(i18n.useTitleJpn), - )), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _showNsfw, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _showNsfw = value; - }); - } - }, - child: Text(i18n.showNsfw), - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _displayAd, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _displayAd = value; - }); - } - }, - child: Text(i18n.displayAd), - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _showTranslatedTag, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _showTranslatedTag = value; - }); - } - }, - child: Text(i18n.showTranslatedTag), - ), - ), - isAndroid || isWindows - ? Container( - padding: - const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _preventScreenCapture, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _preventScreenCapture = value; - }); - } - }, - child: Text(i18n.preventScreenCapture), - ), - ) - : Container(), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: CheckboxMenuButton( - value: _dlUseAvgSpeed, - onChanged: (bool? value) { - if (value != null) { - setState(() { - _dlUseAvgSpeed = value; - }); - } - }, - child: Text(i18n.dlUseAvgSpeed), - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: _buildCache(context), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8), - child: ElevatedButton( - onPressed: () { - reset(context); - }, - child: Text(i18n.reset))), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8), - child: ElevatedButton( - onPressed: () { - save(); - }, - child: Text(i18n.save), - )) - ], - ), - ], - )))); + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const bool.fromEnvironment("skipBaseUrl") != true + ? ListTile( + leading: const Icon(Icons.api), + title: Text(i18n.setServerUrl), + onTap: () { + context.push("/settings/server/url"); + }) + : Container(), + ListTile( + leading: const Icon(Icons.display_settings), + title: Text(i18n.display), + onTap: () { + context.push("/settings/display"); + }), + ListTile( + leading: const Icon(Icons.cached), + title: Text(i18n.cache), + onTap: () { + context.push("/settings/cache"); + }), + auth.isAdmin == true + ? ListTile( + leading: const Icon(Icons.admin_panel_settings), + title: Text(i18n.server), + onTap: () { + context.push("/settings/server"); + }, + ) + : Container(), + ], + )); }, )); } diff --git a/lib/pages/settings/cache.dart b/lib/pages/settings/cache.dart new file mode 100644 index 0000000..2303bca --- /dev/null +++ b/lib/pages/settings/cache.dart @@ -0,0 +1,167 @@ +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 '../../globals.dart'; +import '../../utils/filesize.dart'; + +final _log = Logger("CacheSettingsPage"); + +class CacheSettingsPage extends StatefulWidget { + const CacheSettingsPage({super.key}); + + static const String routeName = '/settings/cache'; + + @override + State createState() => _CacheSettingsPage(); +} + +class _CacheSettingsPage extends State with ThemeModeWidget { + bool _oriEnableImageCache = false; + bool _enableImageCache = false; + @override + void initState() { + super.initState(); + try { + _oriEnableImageCache = prefs.getBool("enableImageCache") ?? true; + _enableImageCache = _oriEnableImageCache; + } catch (e) { + _log.warning("Failed to get enableImageCache:", e); + _oriEnableImageCache = true; + _enableImageCache = true; + } + } + + void fallback(BuildContext context) {} + + void reset(BuildContext context) { + setState(() { + _enableImageCache = true; + }); + } + + Future save() async { + bool re = true; + if (_enableImageCache != _oriEnableImageCache) { + if (!await prefs.setBool("enableImageCache", _enableImageCache)) { + re = false; + _log.warning("Failed to save enableImageCache."); + } else { + _oriEnableImageCache = _enableImageCache; + } + } + return re; + } + + Widget _buildCache(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + final text = SelectableText( + "${i18n.cachedFileSize}${i18n.colon}${getFileSize(imageCaches.size)}"); + final button = ElevatedButton( + onPressed: () { + imageCaches.updateSize(clear: true).then((_) { + setState(() {}); + }).onError((e, _) { + _log.warning("Failed to update image cache size: $e"); + return null; + }); + }, + child: Text(i18n.updateFileSize)); + final cButton = Container( + padding: const EdgeInsets.only(left: 8), + child: ElevatedButton( + onPressed: () { + imageCaches.clear().then((_) { + setState(() {}); + }).onError((e, _) { + _log.warning("Failed to clear image cache: $e"); + return null; + }); + }, + child: Text(i18n.clearCaches))); + final maxWidth = MediaQuery.of(context).size.width; + final useTwoLine = maxWidth <= 500 ? true : false; + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + CheckboxMenuButton( + value: _enableImageCache, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _enableImageCache = value; + }); + } + }, + child: Text(i18n.enableImageCache), + ), + Container( + padding: const EdgeInsets.only(left: 6, top: 4), + child: useTwoLine ? text : Row(children: [text, button, cButton])), + useTwoLine ? Row(children: [button, cButton]) : Container(), + ]); + } + + @override + Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + setCurrentTitle("${i18n.settings} - ${i18n.cache}", + Theme.of(context).primaryColor.value); + return Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () { + fallback(context); + context.canPop() ? context.pop() : context.go("/settings"); + }, + icon: const Icon(Icons.arrow_back), + ), + title: Text(i18n.cache), + actions: [ + buildThemeModeIcon(context), + buildMoreVertSettingsButon(context), + ], + ), + body: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: Container( + padding: MediaQuery.of(context).size.width > 810 + ? const EdgeInsets.symmetric(horizontal: 100) + : null, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: _buildCache(context), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8), + child: ElevatedButton( + onPressed: () { + reset(context); + }, + child: Text(i18n.reset))), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8), + child: ElevatedButton( + onPressed: () { + save(); + }, + child: Text(i18n.save), + )) + ], + ), + ], + )))); + }, + )); + } +} diff --git a/lib/pages/settings/display.dart b/lib/pages/settings/display.dart new file mode 100644 index 0000000..7b5f2d1 --- /dev/null +++ b/lib/pages/settings/display.dart @@ -0,0 +1,355 @@ +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 '../../globals.dart'; +import '../../main.dart'; +import '../../utils.dart'; + +final _log = Logger("DisplaySettingsPage"); + +class DisplaySettingsPage extends StatefulWidget { + const DisplaySettingsPage({super.key}); + + static const String routeName = '/settings/display'; + + @override + State createState() => _DisplaySettingsPage(); +} + +class _DisplaySettingsPage extends State + with ThemeModeWidget { + bool _oriDisplayAd = false; + Lang _oriLang = Lang.system; + bool _oriPreventScreenCapture = false; + bool _oriShowNsfw = false; + bool _oriShowTranslatedTag = false; + bool _oriUseTitleJpn = false; + bool _oriDlUseAvgSpeed = false; + bool _displayAd = false; + Lang _lang = Lang.system; + bool _preventScreenCapture = false; + bool _showNsfw = false; + bool _showTranslatedTag = false; + bool _useTitleJpn = false; + bool _dlUseAvgSpeed = false; + @override + void initState() { + super.initState(); + try { + _oriLang = Lang.values[prefs.getInt("lang") ?? 0]; + _lang = _oriLang; + } catch (e) { + _log.warning("Failed to get lang:", e); + _oriLang = Lang.system; + _lang = Lang.system; + } + try { + _oriUseTitleJpn = prefs.getBool("useTitleJpn") ?? false; + _useTitleJpn = _oriUseTitleJpn; + } catch (e) { + _log.warning("Failed to get useTitleJpn:", e); + _oriUseTitleJpn = false; + _useTitleJpn = false; + } + try { + _oriShowNsfw = prefs.getBool("showNsfw") ?? false; + _showNsfw = _oriShowNsfw; + } catch (e) { + _log.warning("Failed to get showNsfw:", e); + _oriShowNsfw = false; + _showNsfw = false; + } + try { + _oriDisplayAd = prefs.getBool("displayAd") ?? false; + _displayAd = _oriDisplayAd; + } catch (e) { + _log.warning("Failed to get displayAd:", e); + _oriDisplayAd = false; + _displayAd = false; + } + try { + _oriShowTranslatedTag = prefs.getBool("showTranslatedTag") ?? + _oriLang.toLocale().languageCode == "zh"; + _showTranslatedTag = _oriShowTranslatedTag; + } catch (e) { + _log.warning("Failed to get showTranslatedTag:", e); + _oriShowTranslatedTag = false; + _showTranslatedTag = false; + } + try { + _oriPreventScreenCapture = prefs.getBool("preventScreenCapture") ?? false; + _preventScreenCapture = _oriPreventScreenCapture; + } catch (e) { + _log.warning("Failed to get preventScreenCapture:", e); + _oriPreventScreenCapture = false; + _preventScreenCapture = false; + } + try { + _oriDlUseAvgSpeed = prefs.getBool("dlUseAvgSpeed") ?? false; + _dlUseAvgSpeed = _oriDlUseAvgSpeed; + } catch (e) { + _log.warning("Failed to get dlUseAvgSpeed:", e); + _oriDlUseAvgSpeed = false; + _dlUseAvgSpeed = false; + } + } + + void fallback(BuildContext context) { + if (_oriLang != _lang) { + MainApp.of(context).changeLang(_oriLang); + } + } + + void reset(BuildContext context) { + if (_lang != Lang.system) MainApp.of(context).changeLang(Lang.system); + setState(() { + _lang = Lang.system; + _useTitleJpn = false; + _showNsfw = false; + _displayAd = false; + _showTranslatedTag = _oriLang.toLocale().languageCode == "zh"; + _preventScreenCapture = false; + _dlUseAvgSpeed = false; + }); + } + + Future save() async { + bool re = true; + if (_lang != _oriLang && !await prefs.setInt("lang", _lang.index)) { + re = false; + _log.warning("Failed to save lang."); + } else { + _oriLang = _lang; + } + if (_oriUseTitleJpn != _useTitleJpn) { + if (!await prefs.setBool("useTitleJpn", _useTitleJpn)) { + re = false; + _log.warning("Failed to save useTitleJpn."); + } else { + _oriUseTitleJpn = _useTitleJpn; + } + } + if (_oriShowNsfw != _showNsfw) { + if (!await prefs.setBool("showNsfw", _showNsfw)) { + re = false; + _log.warning("Failed to save showNsfw."); + } else { + _oriShowNsfw = _showNsfw; + } + } + if (_oriDisplayAd != _displayAd) { + if (!await prefs.setBool("displayAd", _displayAd)) { + re = false; + _log.warning("Failed to save displayAd."); + } else { + _oriDisplayAd = _displayAd; + } + } + if (_oriShowTranslatedTag != _showTranslatedTag) { + if (!await prefs.setBool("showTranslatedTag", _showTranslatedTag)) { + re = false; + _log.warning("Failed to save showTranslatedTag."); + } else { + _oriShowTranslatedTag = _showTranslatedTag; + } + } + if (_oriPreventScreenCapture != _preventScreenCapture) { + if (!await prefs.setBool("preventScreenCapture", _preventScreenCapture)) { + re = false; + _log.warning("Failed to save preventScreenCapture."); + } else { + _oriPreventScreenCapture = _preventScreenCapture; + } + if (_preventScreenCapture) { + if (!await platformDisplay.enableProtect()) { + _log.warning("Failed to enable protect."); + } + } else { + if (!await platformDisplay.disableProtect()) { + _log.warning("Failed to disable protect."); + } + } + } + if (_dlUseAvgSpeed != _oriDlUseAvgSpeed) { + if (!await prefs.setBool("dlUseAvgSpeed", _dlUseAvgSpeed)) { + re = false; + _log.warning("Failed to save dlUseAvgSpeed."); + } else { + _oriDlUseAvgSpeed = _dlUseAvgSpeed; + } + } + return re; + } + + @override + Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + setCurrentTitle("${i18n.settings} - ${i18n.display}", + Theme.of(context).primaryColor.value); + return Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () { + fallback(context); + context.canPop() ? context.pop() : context.go("/settings"); + }, + icon: const Icon(Icons.arrow_back), + ), + title: Text(i18n.display), + actions: [ + buildThemeModeIcon(context), + buildMoreVertSettingsButon(context), + ], + ), + body: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: Container( + padding: MediaQuery.of(context).size.width > 810 + ? const EdgeInsets.symmetric(horizontal: 100) + : null, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: + const EdgeInsets.symmetric(vertical: 8), + child: DropdownMenu( + initialSelection: _lang, + onSelected: (value) { + if (value != null) { + setState(() { + _lang = value; + }); + MainApp.of(context).changeLang(_lang); + } + }, + label: Text(i18n.lang), + dropdownMenuEntries: Lang.values + .map((e) => DropdownMenuEntry( + value: e, + label: e == Lang.system + ? i18n.systemLang + : e.langName)) + .toList(), + leadingIcon: const Icon(Icons.language), + )), + Container( + padding: + const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _useTitleJpn, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _useTitleJpn = value; + }); + } + }, + child: Text(i18n.useTitleJpn), + )), + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _showNsfw, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _showNsfw = value; + }); + } + }, + child: Text(i18n.showNsfw), + ), + ), + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _displayAd, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _displayAd = value; + }); + } + }, + child: Text(i18n.displayAd), + ), + ), + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _showTranslatedTag, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _showTranslatedTag = value; + }); + } + }, + child: Text(i18n.showTranslatedTag), + ), + ), + isAndroid || isWindows + ? Container( + padding: + const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _preventScreenCapture, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _preventScreenCapture = value; + }); + } + }, + child: Text(i18n.preventScreenCapture), + ), + ) + : Container(), + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _dlUseAvgSpeed, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _dlUseAvgSpeed = value; + }); + } + }, + child: Text(i18n.dlUseAvgSpeed), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8), + child: ElevatedButton( + onPressed: () { + reset(context); + }, + child: Text(i18n.reset))), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8), + child: ElevatedButton( + onPressed: () { + save(); + }, + child: Text(i18n.save), + )) + ], + ), + ], + )))); + }, + )); + } +} diff --git a/lib/pages/server_settings.dart b/lib/pages/settings/server.dart similarity index 98% rename from lib/pages/server_settings.dart rename to lib/pages/settings/server.dart index d55a58b..4c2e968 100644 --- a/lib/pages/server_settings.dart +++ b/lib/pages/settings/server.dart @@ -4,19 +4,19 @@ 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/labeled_checkbox.dart'; -import '../components/number_field.dart'; -import '../components/string_list_field.dart'; -import '../components/string_map_field.dart'; -import '../globals.dart'; -import '../platform/ua.dart'; +import '../../api/config.dart'; +import '../../components/labeled_checkbox.dart'; +import '../../components/number_field.dart'; +import '../../components/string_list_field.dart'; +import '../../components/string_map_field.dart'; +import '../../globals.dart'; +import '../../platform/ua.dart'; final _log = Logger("ServerSettingsPage"); class ServerSettingsPage extends StatefulWidget { const ServerSettingsPage({super.key}); - static get routeName => "/server_settings"; + static get routeName => "/settings/server"; @override State createState() => _ServerSettingsPage(); @@ -121,7 +121,7 @@ class _ServerSettingsPage extends State final i18n = AppLocalizations.of(context)!; final cs = Theme.of(context).colorScheme; if (isTop(context)) { - setCurrentTitle(i18n.serverSettings, cs.primary.value); + setCurrentTitle("${i18n.settings} - ${i18n.server}", cs.primary.value); } return Scaffold( appBar: isLoading @@ -132,7 +132,7 @@ class _ServerSettingsPage extends State context.canPop() ? context.pop() : context.go("/"); }, ), - title: Text(i18n.serverSettings), + title: Text(i18n.server), actions: [ buildThemeModeIcon(context), buildMoreVertSettingsButon(context), @@ -191,7 +191,7 @@ class _ServerSettingsPage extends State context.canPop() ? context.pop() : context.go("/"); }, ), - title: Text(i18n.serverSettings), + title: Text(i18n.server), actions: [ buildThemeModeIcon(context), buildMoreVertSettingsButon(context), diff --git a/lib/pages/set_server.dart b/lib/pages/settings/server_url.dart similarity index 84% rename from lib/pages/set_server.dart rename to lib/pages/settings/server_url.dart index a8be6ae..3b835df 100644 --- a/lib/pages/set_server.dart +++ b/lib/pages/settings/server_url.dart @@ -3,20 +3,21 @@ import 'package:flutter/scheduler.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; -import '../globals.dart'; +import '../../globals.dart'; -final _log = Logger("SetServerPage"); +final _log = Logger("ServerUrlSettingsPage"); -class SetServerPage extends StatefulWidget { - const SetServerPage({super.key}); +class ServerUrlSettingsPage extends StatefulWidget { + const ServerUrlSettingsPage({super.key}); - static const String routeName = '/set_server'; + static const String routeName = '/settings/server/url'; @override - State createState() => _SetServerPageState(); + State createState() => _ServerUrlSettingsPage(); } -class _SetServerPageState extends State with ThemeModeWidget { +class _ServerUrlSettingsPage extends State + with ThemeModeWidget { String _serverUrl = ""; String _apiPath = "/api/"; bool _isValid = false; @@ -65,6 +66,7 @@ class _SetServerPageState extends State with ThemeModeWidget { @override Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; bool? skipBaseUrl = const bool.fromEnvironment("skipBaseUrl"); if (skipBaseUrl == true) { SchedulerBinding.instance.addPostFrameCallback((_) { @@ -76,9 +78,11 @@ class _SetServerPageState extends State with ThemeModeWidget { buildThemeModeIcon(context), ]; if (hasBaseUrl) actions.add(buildMoreVertSettingsButon(context)); + setCurrentTitle("${i18n.settings} - ${i18n.setServerUrl}", + Theme.of(context).primaryColor.value); return Scaffold( appBar: AppBar( - title: Text(AppLocalizations.of(context)!.setServerUrl), + title: Text(i18n.setServerUrl), leading: hasBaseUrl ? IconButton( icon: const Icon(Icons.arrow_back), @@ -103,7 +107,7 @@ class _SetServerPageState extends State with ThemeModeWidget { child: TextFormField( decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: AppLocalizations.of(context)!.serverHost, + labelText: i18n.serverHost, ), initialValue: _serverUrl, onChanged: _serverUrlChanged, @@ -113,7 +117,7 @@ class _SetServerPageState extends State with ThemeModeWidget { child: TextFormField( decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: AppLocalizations.of(context)!.apiPath, + labelText: i18n.apiPath, ), initialValue: _apiPath, onChanged: _apiPathChanged, @@ -133,7 +137,7 @@ class _SetServerPageState extends State with ThemeModeWidget { }); } : null, - child: Text(AppLocalizations.of(context)!.save)), + child: Text(i18n.save)), ], ))), );