diff --git a/ui/flutter/lib/i18n/messages.dart b/ui/flutter/lib/i18n/messages.dart index b0386d6d..e2e30395 100644 --- a/ui/flutter/lib/i18n/messages.dart +++ b/ui/flutter/lib/i18n/messages.dart @@ -6,6 +6,10 @@ Locale toLocale(String key) { return Locale(arr[0], arr[1]); } +String getLocaleKey(Locale locale) { + return '${locale.languageCode}_${locale.countryCode}'; +} + final messages = _Messages(); const fallbackLocale = Locale('en', 'US'); @@ -21,7 +25,8 @@ class _Messages extends Translations { 'create.title': '创建任务', 'create.downloadLink': '下载链接', 'create.downloadLinkValid': '请输入下载链接', - 'create.downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET', + 'create.downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append', + 'create.downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处', 'create.download': '下载', 'create.error.noStoragePermission': '需要开启存储权限', 'create.selectDir': '选择目录', @@ -51,7 +56,9 @@ class _Messages extends Translations { 'create.downloadLink': 'Download Link', 'create.downloadLinkValid': 'Please enter the download link', 'create.downloadLinkHit': - 'Please enter the download link, HTTP/HTTPS/MAGNET supported', + 'Please enter the download link, HTTP/HTTPS/MAGNET supported@append', + 'create.downloadLinkHitDesktop': + ', or drag the torrent file here directly', 'create.download': 'Download', 'create.error.noStoragePermission': 'Storage permission required', 'create.selectDir': 'Select Directory', diff --git a/ui/flutter/lib/main.dart b/ui/flutter/lib/main.dart index 0397b350..6e3d8531 100644 --- a/ui/flutter/lib/main.dart +++ b/ui/flutter/lib/main.dart @@ -26,7 +26,7 @@ void main() async { darkTheme: GopeedTheme.dark, themeMode: Setting.instance.themeMode, translations: messages, - locale: Setting.instance.locale, + locale: toLocale(Setting.instance.locale), fallbackLocale: fallbackLocale, getPages: Routes.routes, )); diff --git a/ui/flutter/lib/pages/create/create_controller.dart b/ui/flutter/lib/pages/create/create_controller.dart index 8d5658fa..ba833f24 100644 --- a/ui/flutter/lib/pages/create/create_controller.dart +++ b/ui/flutter/lib/pages/create/create_controller.dart @@ -1,5 +1,3 @@ import 'package:get/get.dart'; -class CreateController extends GetxController { - var currentIndex = 0.obs; -} +class CreateController extends GetxController {} diff --git a/ui/flutter/lib/pages/create/create_view.dart b/ui/flutter/lib/pages/create/create_view.dart index b082a80e..4cb5151a 100644 --- a/ui/flutter/lib/pages/create/create_view.dart +++ b/ui/flutter/lib/pages/create/create_view.dart @@ -1,3 +1,4 @@ +import 'package:desktop_drop/desktop_drop.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../api/api.dart'; @@ -32,65 +33,75 @@ class CreateView extends GetView { actions: [Container()], title: Text('create.title'.tr), ), - body: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), - child: Form( - key: _resolveFormKey, - autovalidateMode: AutovalidateMode.always, - child: Column( - children: [ - TextFormField( - autofocus: true, - controller: _urlController, - minLines: 1, - maxLines: 15, - decoration: InputDecoration( - hintText: 'create.downloadLinkHit'.tr, - labelText: 'create.downloadLink'.tr, - icon: const Icon(Icons.link)), - validator: (v) { - return v!.trim().isNotEmpty - ? null - : 'create.downloadLinkValid'.tr; - }), - // 登录按钮 - Center( - child: Padding( - padding: const EdgeInsets.all(15.0), - child: ConstrainedBox( - constraints: const BoxConstraints.tightFor( - width: 150, - height: 40, - ), - child: RoundedLoadingButton( - color: Get.theme.colorScheme.secondary, - onPressed: () async { - if (_resolveFormKey.currentState!.validate()) { - _confirmController.start(); - try { - final res = await resolve(Request( - url: _urlController.text, - )); - await _showResolveDialog(res); - } catch (e) { - Get.snackbar('error'.tr, e.toString()); - } finally { - _confirmController.reset(); + body: DropTarget( + onDragDone: (details) { + _urlController.text = details.files[0].path; + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), + child: Form( + key: _resolveFormKey, + autovalidateMode: AutovalidateMode.always, + child: Column( + children: [ + TextFormField( + autofocus: true, + controller: _urlController, + minLines: 1, + maxLines: 15, + decoration: InputDecoration( + hintText: _hitText(), + labelText: 'create.downloadLink'.tr, + icon: const Icon(Icons.link)), + validator: (v) { + return v!.trim().isNotEmpty + ? null + : 'create.downloadLinkValid'.tr; + }), + Center( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: ConstrainedBox( + constraints: const BoxConstraints.tightFor( + width: 150, + height: 40, + ), + child: RoundedLoadingButton( + color: Get.theme.colorScheme.secondary, + onPressed: () async { + if (_resolveFormKey.currentState!.validate()) { + _confirmController.start(); + try { + final res = await resolve(Request( + url: _urlController.text, + )); + await _showResolveDialog(res); + } catch (e) { + Get.snackbar('error'.tr, e.toString()); + } finally { + _confirmController.reset(); + } } - } - }, - controller: _confirmController, - child: Text('confirm'.tr), - ), - )), - ), - ], + }, + controller: _confirmController, + child: Text('confirm'.tr), + ), + )), + ), + ], + ), ), ), ), ); } + String _hitText() { + return 'create.downloadLinkHit'.trParams({ + 'append': Util.isDesktop() ? 'create.downloadLinkHitDesktop'.tr : '', + }); + } + Future _showResolveDialog(Resource res) async { final createFormKey = GlobalKey(); final pathController = diff --git a/ui/flutter/lib/pages/setting/setting_view.dart b/ui/flutter/lib/pages/setting/setting_view.dart index 446f88a1..6f942317 100644 --- a/ui/flutter/lib/pages/setting/setting_view.dart +++ b/ui/flutter/lib/pages/setting/setting_view.dart @@ -16,8 +16,15 @@ class SettingView extends GetView { Widget build(BuildContext context) { Timer? timer; debounceSave() { + var completer = Completer(); timer?.cancel(); - timer = Timer(const Duration(milliseconds: 500), Setting.instance.save); + timer = Timer(const Duration(milliseconds: 500), () { + Setting.instance + .save() + .then(completer.complete) + .onError(completer.completeError); + }); + return completer.future; } return Scaffold( @@ -39,12 +46,12 @@ class SettingView extends GetView { () => DropdownButton( value: controller.setting.value.themeMode, isDense: true, - onChanged: (value) { + onChanged: (value) async { controller.setting.value.themeMode = value!; controller.clearTapStatus(); Get.changeThemeMode(value); - debounceSave(); + await debounceSave(); }, items: ThemeMode.values .map((e) => DropdownMenuItem( @@ -57,14 +64,14 @@ class SettingView extends GetView { () => controller.setting.value.downloadDir, () { final downloadDirController = TextEditingController( text: controller.setting.value.downloadDir); - downloadDirController.addListener(() { + downloadDirController.addListener(() async { if (downloadDirController.text != controller.setting.value.downloadDir) { controller.setting.value.downloadDir = downloadDirController.text; controller.clearTapStatus(); - debounceSave(); + await debounceSave(); } }); return DirectorySelector( @@ -77,14 +84,14 @@ class SettingView extends GetView { () { final connectionsController = TextEditingController( text: controller.setting.value.connections.toString()); - connectionsController.addListener(() { + connectionsController.addListener(() async { if (connectionsController.text.isNotEmpty && connectionsController.text != controller.setting.value.connections.toString()) { controller.setting.value.connections = int.parse(connectionsController.text); - debounceSave(); + await debounceSave(); } }); @@ -100,20 +107,20 @@ class SettingView extends GetView { _ConfigItem( 'setting.locale'.tr, () => _getLocaleName(controller.setting.value.locale), - () => DropdownButton( + () => DropdownButton( value: controller.setting.value.locale, isDense: true, - onChanged: (value) { + onChanged: (value) async { controller.setting.value.locale = value!; controller.clearTapStatus(); - Get.updateLocale(value); + Get.updateLocale(toLocale(value)); - debounceSave(); + await debounceSave(); }, items: _getLocales() - .map((e) => DropdownMenuItem( - value: toLocale(e - .substring(_settingLocaleKey.length)), + .map((e) => DropdownMenuItem( + value: + e.substring(_settingLocaleKey.length), child: Text(e.tr), )) .toList(), @@ -164,14 +171,18 @@ class SettingView extends GetView { final _settingLocaleKey = 'setting.locale.'; List _getLocales() { - return messages.keys[fallbackLocale.toString()]!.entries + return messages.keys[getLocaleKey(fallbackLocale)]!.entries .where((e) => e.key.startsWith(_settingLocaleKey)) .map((e) => e.key) .toList(); } - String _getLocaleName(Locale locale) { - return '$_settingLocaleKey${locale.toString()}'.tr; + String _getLocaleName(String locale) { + final localeKey = '$_settingLocaleKey${locale.toString()}'; + if (messages.keys[locale]?.containsKey(localeKey) ?? false) { + return localeKey.tr; + } + return '$_settingLocaleKey$locale'.tr; } } diff --git a/ui/flutter/lib/setting/setting.dart b/ui/flutter/lib/setting/setting.dart index 0dbce289..f0968b13 100644 --- a/ui/flutter/lib/setting/setting.dart +++ b/ui/flutter/lib/setting/setting.dart @@ -14,14 +14,16 @@ class Setting { int port = 0; int connections = 0; String downloadDir = ""; - ThemeMode themeMode = ThemeMode.system; - Locale locale = ui.window.locale; + late ThemeMode themeMode; + late String locale; // singleton pattern static Setting? _instance; static Setting get instance { _instance ??= Setting._internal(); + _instance!.themeMode = ThemeMode.system; + _instance!.locale = getLocaleKey(ui.window.locale); return _instance!; } @@ -39,7 +41,7 @@ class Setting { } final locale = config.extra?['locale']; if (locale != null) { - this.locale = toLocale(locale); + this.locale = locale; } if (Util.isWeb()) { @@ -67,7 +69,7 @@ class Setting { downloadDir: downloadDir, extra: { 'themeMode': themeMode.name, - 'locale': locale.toString(), + 'locale': locale, }, ); await putConfig(config); diff --git a/ui/flutter/macos/Podfile.lock b/ui/flutter/macos/Podfile.lock index e5092633..1569144a 100644 --- a/ui/flutter/macos/Podfile.lock +++ b/ui/flutter/macos/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - desktop_drop (0.0.1): + - FlutterMacOS - file_selector_macos (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) @@ -12,6 +14,7 @@ PODS: - FlutterMacOS DEPENDENCIES: + - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - macos_secure_bookmarks (from `Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos`) @@ -20,6 +23,8 @@ DEPENDENCIES: - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) EXTERNAL SOURCES: + desktop_drop: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos file_selector_macos: :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos FlutterMacOS: @@ -34,6 +39,7 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: + desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 file_selector_macos: f1b08a781e66103e3ba279fd5d4024a2478b3af6 FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 macos_secure_bookmarks: cff041c4b377fa00941bb901aaec4d27fac5edf2 diff --git a/ui/flutter/pubspec.lock b/ui/flutter/pubspec.lock index 869b271d..8e8ad0f6 100644 --- a/ui/flutter/pubspec.lock +++ b/ui/flutter/pubspec.lock @@ -183,6 +183,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.4" + desktop_drop: + dependency: "direct main" + description: + name: desktop_drop + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.3" dio: dependency: "direct main" description: diff --git a/ui/flutter/pubspec.yaml b/ui/flutter/pubspec.yaml index d7e69e0a..f28da324 100644 --- a/ui/flutter/pubspec.yaml +++ b/ui/flutter/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: permission_handler: ^10.1.0 external_path: ^1.0.3 logger: ^1.1.0 + desktop_drop: ^0.3.3 dev_dependencies: flutter_test: