diff --git a/lib/auth.dart b/lib/auth.dart index b4ea56a..1de1018 100644 --- a/lib/auth.dart +++ b/lib/auth.dart @@ -15,6 +15,12 @@ class AuthInfo { bool _checked = false; bool get checked => _checked; + void clear() { + _user = null; + _status = null; + _checked = false; + } + Future getServerStatus() async { _status = (await api.getStatus()).unwrap(); } diff --git a/lib/globals.dart b/lib/globals.dart index 1d26a24..6c19a9b 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -13,6 +13,7 @@ import 'auth.dart'; import 'config/base.dart'; import 'config/shared_preferences.dart'; import 'config/windows.dart'; +import 'main.dart'; import 'platform/path.dart'; import 'utils.dart'; @@ -89,6 +90,7 @@ bool tryInitApi(BuildContext context) { if (_api != null && _api!.baseUrl == baseUrl) { return true; } + auth.clear(); initApi(baseUrl); return true; } @@ -108,3 +110,41 @@ final AuthInfo auth = AuthInfo(); final Path platformPath = Path(); final GlobalKey rootScaffoldMessengerKey = GlobalKey(); + +enum MoreVertSettings { + setServerUrl, +} + +void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) { + switch (value) { + case MoreVertSettings.setServerUrl: + context.go("/set_server"); + break; + default: + break; + } +} + +ThemeMode themeModeNext(ThemeMode mode) { + if (mode == ThemeMode.system) return ThemeMode.light; + if (mode == ThemeMode.dark) return ThemeMode.system; + return ThemeMode.dark; +} + +mixin ThemeModeWidget on State { + @protected + Widget buildThemeModeIcon(BuildContext context) { + final mode = MainApp.of(context).themeMode; + return IconButton( + onPressed: () { + final n = themeModeNext(mode); + MainApp.of(context).changeThemeMode(n); + setState(() {}); + }, + icon: Icon(mode == ThemeMode.system + ? Icons.brightness_auto + : mode == ThemeMode.dark + ? Icons.dark_mode + : Icons.light_mode)); + } +} diff --git a/lib/home.dart b/lib/home.dart index 751d984..3bf2771 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -8,12 +8,6 @@ import 'main.dart'; final _log = Logger("HomePage"); -ThemeMode _themeModeNext(ThemeMode mode) { - if (mode == ThemeMode.system) return ThemeMode.light; - if (mode == ThemeMode.dark) return ThemeMode.system; - return ThemeMode.dark; -} - class HomePage extends HookWidget { const HomePage({Key? key}) : super(key: key); @@ -41,7 +35,7 @@ class HomePage extends HookWidget { actions: [ IconButton( onPressed: () { - final n = _themeModeNext(mode.value); + final n = themeModeNext(mode.value); MainApp.of(context).changeThemeMode(n); mode.value = n; }, @@ -50,7 +44,21 @@ class HomePage extends HookWidget { : mode.value == ThemeMode.dark ? Icons.dark_mode : Icons.light_mode)), - const IconButton(onPressed: null, icon: Icon(Icons.more_vert)), + PopupMenuButton( + icon: const Icon(Icons.more_vert), + onSelected: (MoreVertSettings value) { + onMoreVertSettingsSelected(context, value); + }, + itemBuilder: (BuildContext build) { + var list = >[]; + if (const bool.fromEnvironment("skipBaseUrl") != true) { + list.add(PopupMenuItem( + value: MoreVertSettings.setServerUrl, + child: Text(AppLocalizations.of(build)!.setServerUrl))); + } + return list; + }, + ), ], ), body: const Center( diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ab3c381..553c296 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -9,5 +9,6 @@ "titleBar": "EH Downloader", "incorrectUserPassword": "Incorrect username or password.", "networkError": "Network error.", - "internalError": "Some internal error occurred. Please send the log file to the developer." + "internalError": "Some internal error occurred. Please send the log file to the developer.", + "setServerUrl": "Server URL Settings" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 48e9d1f..974a014 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -9,5 +9,6 @@ "titleBar": "EH 下载器", "incorrectUserPassword": "不正确的用户名或密码。", "networkError": "网络错误。", - "internalError": "出现了内部错误,请将日志发送给开发者。" + "internalError": "出现了内部错误,请将日志发送给开发者。", + "setServerUrl": "服务器地址设置" } diff --git a/lib/login.dart b/lib/login.dart index 9bd41c4..1b9eba6 100644 --- a/lib/login.dart +++ b/lib/login.dart @@ -16,7 +16,7 @@ class LoginPage extends StatefulWidget { State createState() => _LoginPageState(); } -class _LoginPageState extends State { +class _LoginPageState extends State with ThemeModeWidget { final _formKey = GlobalKey(); String _username = ""; String _password = ""; @@ -102,8 +102,16 @@ class _LoginPageState extends State { tryInitApi(context); _checkStatus(context); return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.login), + actions: [ + buildThemeModeIcon(context), + ], + ), body: Container( - padding: const EdgeInsets.symmetric(horizontal: 100), + padding: MediaQuery.of(context).size.width > 810 + ? const EdgeInsets.symmetric(horizontal: 100) + : null, child: Form( key: _formKey, child: Column( diff --git a/lib/set_server.dart b/lib/set_server.dart index 0fbcfe7..dd161ab 100644 --- a/lib/set_server.dart +++ b/lib/set_server.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; 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'; +final _log = Logger("SetServerPage"); + class SetServerPage extends StatefulWidget { const SetServerPage({Key? key}) : super(key: key); @@ -13,7 +16,7 @@ class SetServerPage extends StatefulWidget { State createState() => _SetServerPageState(); } -class _SetServerPageState extends State { +class _SetServerPageState extends State with ThemeModeWidget { String _serverUrl = ""; String _apiPath = "/api/"; bool _isValid = false; @@ -30,7 +33,7 @@ class _SetServerPageState extends State { _apiPath = url.path; _isValid = true; } catch (e) { - // Do nothing. + _log.warning("Failed to parse baseUrl:", e); } } } @@ -68,9 +71,26 @@ class _SetServerPageState extends State { context.go("/"); }); } + final bool hasBaseUrl = prefs.getString('baseUrl') != null; return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.setServerUrl), + leading: hasBaseUrl + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + context.canPop() ? context.pop() : context.go("/"); + }, + ) + : null, + actions: [ + buildThemeModeIcon(context), + ], + ), body: Container( - padding: const EdgeInsets.symmetric(horizontal: 100), + padding: MediaQuery.of(context).size.width > 810 + ? const EdgeInsets.symmetric(horizontal: 100) + : null, child: Form( key: _formKey, child: Column(