diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd283bd1..80e9f93b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,52 @@ # Gopeed contributors guide -Firstly, thank you for your interest in contributing to Gopeed. This guide will help you better participate in the development of Gopeed. +Firstly, thank you for your interest in contributing to Gopeed. This guide will help you better +participate in the development of Gopeed. ## Branch description -This project only has one main branch, namely the `main` branch. If you want to participate in the development of Gopeed, please fork this project first, and then develop in your fork project. After development is completed, submit a PR to this project and merge it into the `main` branch. +This project only has one main branch, namely the `main` branch. If you want to participate in the +development of Gopeed, please fork this project first, and then develop in your fork project. After +development is completed, submit a PR to this project and merge it into the `main` branch. ## Local development -It is recommended to develop and debug through the web. First, start the backend service, and start it by the command line `go run cmd/api/main.go`, the default port of the service is `9999`, and then start the front-end flutter project in `debug` mode to run. +It is recommended to develop and debug through the web. First, start the backend service, and start +it by the command line `go run cmd/api/main.go`, the default port of the service is `9999`, and then +start the front-end flutter project in `debug` mode to run. ## Translation -The internationalization files of Gopeed are located in the `ui/flutter/lib/i18n/langs` directory. You only need to add the corresponding language file in this directory. +The internationalization files of Gopeed are located in the `ui/flutter/assets/locales` directory. +You only need to add the corresponding language file in this directory. -> Tip: Please refer to `en_us.dart` for translation. \ No newline at end of file +Generate locales after you edit locales/internationalization: + +``` +get generate locales +``` + +Please refer to `en_us.dart` for translation. + +## flutter development + +Don't forget to run`dart format .`before you commit to keep your code in standard dart format + +Turn on build_runner watcher if you want to edit api/models: + +``` +flutter pub run build_runner watch +``` + + +get-cli commands usages: + +``` + create: + controller: Generate controller + page: Use to generate pages + view: Generate view + generate: + locales: Generate translation file from json files + model: generate Class model from json +``` diff --git a/CONTRIBUTING_zh-CN.md b/CONTRIBUTING_zh-CN.md index 7b01fb20..8e03ef83 100644 --- a/CONTRIBUTING_zh-CN.md +++ b/CONTRIBUTING_zh-CN.md @@ -4,14 +4,45 @@ ## 分支说明 -本项目只有一个主分支,即 `main` 分支,如果您想要参与到 Gopeed 的开发中来,请先 fork 本项目,然后在您的 fork 项目中进行开发,开发完成后再向本项目提交 PR,合并到 `main` 分支。 +本项目只有一个主分支,即 `main` 分支,如果您想要参与到 Gopeed 的开发中来,请先 fork 本项目,然后在您的 fork 项目中进行开发,开发完成后再向本项目提交 +PR,合并到 `main` 分支。 ## 本地开发 -建议通过 web 端进行开发调试,首先启动后端服务,通过命令行 `go run cmd/api/main.go` 启动 ,服务启动默认端口为 `9999`,然后以 `debug` 模式启动前端 flutter 项目即可运行。 +建议通过 web 端进行开发调试,首先启动后端服务,通过命令行 `go run cmd/api/main.go` 启动 ,服务启动默认端口为 `9999`,然后以 `debug` 模式启动前端 +flutter 项目即可运行。 ## 翻译 + +Gopeed 的国际化文件位于 `ui/flutter/assets/locales` 目录下,只需要在该目录下添加对应的语言文件即可。 -Gopeed 的国际化文件位于 `ui/flutter/lib/i18n/langs` 目录下,只需要在该目录下添加对应的语言文件即可。 +编辑locales/internationalization后请运行以下命令: + +``` +get generate locales +``` + +请注意以 `en_US.json` 为参照进行翻译。 + +## flutter development + +每次提交前请务必运行`dart format .` + +如果要编辑api/models,请打开build_runner watcher: + +``` +flutter pub run build_runner watch +``` + +适用get-cli 命令: + +``` + create: + controller: Generate controller + page: Use to generate pages + view: Generate view + generate: + locales: Generate translation file from json files + model: generate Class model from json +``` -> 请注意以 `en_us.dart` 为参照进行翻译。 diff --git a/ui/flutter/.fvm/flutter_sdk b/ui/flutter/.fvm/flutter_sdk new file mode 120000 index 00000000..ad024458 --- /dev/null +++ b/ui/flutter/.fvm/flutter_sdk @@ -0,0 +1 @@ +C:/Users/huget/fvm/versions/3.7.0 \ No newline at end of file diff --git a/ui/flutter/.fvm/fvm_config.json b/ui/flutter/.fvm/fvm_config.json new file mode 100644 index 00000000..c2dd9f53 --- /dev/null +++ b/ui/flutter/.fvm/fvm_config.json @@ -0,0 +1,4 @@ +{ + "flutterSdkVersion": "3.7.0", + "flavors": {} +} \ No newline at end of file diff --git a/ui/flutter/README.md b/ui/flutter/README.md deleted file mode 100644 index 9778622f..00000000 --- a/ui/flutter/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - - - -Don't forget to run`dart format .`before you commit to keep your code in standard dart format \ No newline at end of file diff --git a/ui/flutter/assets/locales/en_US.json b/ui/flutter/assets/locales/en_US.json new file mode 100644 index 00000000..296ada73 --- /dev/null +++ b/ui/flutter/assets/locales/en_US.json @@ -0,0 +1,53 @@ +{ + "label": "English", + "error": "Error", + "tip": "Tip", + "confirm": "Confirm", + "cancel": "Cancel", + "selectAll": "Select All", + "task": "Task", + "downloading": "downloading", + "downloaded": "downloaded", + "setting": "Setting", + "create": "Create Task", + "downloadLink": "Download Link", + "downloadLinkValid": "Please enter the download link", + "downloadLinkHit": "Please enter the download link, HTTP/HTTPS/MAGNET supported@append", + "downloadLinkHitDesktop": ", or drag the torrent file here directly", + "download": "Download", + "noStoragePermission": "Storage permission required", + "selectFile": "Select File", + "basic": "Basic", + "advanced": "Advanced", + "general": "General", + "downloadDir": "Download Directory", + "downloadDirValid": "Please select the download directory", + "connections": "Connections", + "items": "@count items", + "subscribeTracker": "Subscribe Tracker", + "subscribeFail": "Subscribe failed, please check network or try again later", + "update": "Update", + "updateDaily": "Update daily", + "lastUpdate": "Last update: @time", + "addTracker": "Add Tracker", + "addTrackerHit": "Please enter the tracker server url, one per line", + "ui": "UI", + "theme": "Theme", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "locale": "Language", + "about": "About", + "homepage": "Homepage", + "version": "Version", + "protocol": "Protocol", + "port": "Port", + "apiToken": "API Token", + "notSet": "NS", + "set": "SET", + "effectAfterRestart": "Effect after restart", + "deleteTask": "Delete Task", + "deleteTaskTip": "Keep downloaded files", + "delete": "Delete" +} + diff --git a/ui/flutter/assets/locales/fa_IR.json b/ui/flutter/assets/locales/fa_IR.json new file mode 100644 index 00000000..5b43958e --- /dev/null +++ b/ui/flutter/assets/locales/fa_IR.json @@ -0,0 +1,53 @@ +{ + "label": "فارسی", + "error": "اررور", + "tip": "Tip", + "confirm": "تایید", + "cancel": "انصراف", + "selectAll": "انتخاب همه", + "task": "کار", + "downloading": "downloading", + "downloaded": "downloaded", + "setting": "تنظیمات", + "title": "ایجاد کار", + "downloadLink": "لینک دانلود", + "downloadLinkValid": "لطفا لینک دانلود را وارد کنید", + "downloadLinkHit": "Please enter the download link, HTTP/HTTPS/MAGNET supported@append", + "downloadLinkHitDesktop": ", or drag the torrent file here directly", + "download": "دانلود", + "noStoragePermission": "مجوز ذخیره سازی مورد نیاز است", + "selectFile": "انتخاب فایل", + "basic": "پایه", + "advanced": "پیشرفته", + "general": "عمومی", + "downloadDir": "دایرکتوری دانلود", + "downloadDirValid": "لطفا دایرکتوری دانلود را انتخاب کنید", + "connections": "اتصالات", + "items": "@count آیتم ها", + "subscribeTracker": "Subscribe Tracker", + "subscribeFail": "Subscribe failed, please check network or try again later", + "update": "بروزرسانی", + "updateDaily": "بروزرسانی روزانه", + "lastUpdate": "Last update: @time", + "addTracker": "Add Tracker", + "addTrackerHit": "Please enter the tracker server url, one per line", + "ui": "رابط کاربری", + "theme": "پوسته", + "themeSystem": "سیستم", + "themeLight": "روشن", + "themeDark": "تاریک", + "locale": "Language", + "about": "درباره ی", + "homepage": "صفحه اصلی", + "version": "نسخه", + "protocol": "پروتکل", + "port": "پورت", + "apiToken": "API توکن", + "notSet": "NS", + "set": "SET", + "effectAfterRestart": "Effect after restart", + "deleteTask": "پاک کردن کار", + "deleteTaskTip": "فایل های دانلود شده را نگه دارد", + "delete": "پاک کردن" +} + diff --git a/ui/flutter/assets/locales/ru_RU.json b/ui/flutter/assets/locales/ru_RU.json new file mode 100644 index 00000000..99cfd7f5 --- /dev/null +++ b/ui/flutter/assets/locales/ru_RU.json @@ -0,0 +1,52 @@ +{ + "label": "Русский(Россия)", + "error": "Ошибка", + "tip": "Подсказка", + "confirm": "Подтвердить", + "cancel": "Отмена", + "selectAll": "Выбрать все", + "task": "Задачи", + "downloading": "downloading", + "downloaded": "downloaded", + "setting": "Настройки", + "title": "Создать задачу", + "downloadLink": "Ссылка для скачивание", + "downloadLinkValid": "Пожалуйста, введите ссылку для скачивания", + "downloadLinkHit": "Пожалуйста, введите ссылку для скачивания, HTTP/HTTPS/MAGNET supported@append", + "downloadLinkHitDesktop": ", или перетащите сюда торрент-файл", + "download": "Скачать", + "noStoragePermission": "Требуется доступ к хранилищу", + "selectFile": "Выберать файл", + "basic": "Основные параметры", + "advanced": "Расширенные", + "general": "Общие", + "downloadDir": "Папка загрузки", + "downloadDirValid": "Пожалуйста, выберите папку для загрузки", + "connections": "Соединений", + "items": "@подсчет элементов", + "subscribeTracker": "Подпись трекера", + "subscribeFail": "Подписка не удалась, пожалуйста, проверьте сеть или повторите попытку позже", + "update": "Обновить", + "updateDaily": "Обновлять ежедневно", + "lastUpdate": "Последнее обновление: @время", + "addTracker": "Добавить трекер", + "addTrackerHit": "Пожалуйста, введите url трекера, по одному в строке", + "ui": "UI", + "theme": "Тема", + "themeSystem": "Системная", + "themeLight": "Светлая", + "themeDark": "Темная", + "locale": "Язык", + "about": "О приложении", + "homepage": "Домашняя страница", + "version": "Версия", + "protocol": "Протокол", + "port": "Порт", + "apiToken": "API Токен", + "notSet": "Не установлено", + "set": "Установлено", + "effectAfterRestart": "Эффект после перезагрузки", + "deleteTask": "Удалить задачу", + "deleteTaskTip": "Сохранить загруженные файлы", + "delete": "Удалить" +} diff --git a/ui/flutter/assets/locales/zh_CN.json b/ui/flutter/assets/locales/zh_CN.json new file mode 100644 index 00000000..70043ca5 --- /dev/null +++ b/ui/flutter/assets/locales/zh_CN.json @@ -0,0 +1,52 @@ +{ + "label": "中文(简体)", + "error": "错误", + "tip": "提示", + "confirm": "确认", + "cancel": "取消", + "selectAll": "全选", + "task": "任务", + "downloading": "下载中", + "downloaded": "已完成", + "setting": "设置", + "title": "创建任务", + "downloadLink": "下载链接", + "downloadLinkValid": "请输入下载链接", + "downloadLinkHit": "请输入下载链接,支持 HTTP/HTTPS/MAGNET@append", + "downloadLinkHitDesktop": ",也可以直接拖拽种子文件到此处", + "download": "下载", + "noStoragePermission": "需要开启存储权限", + "selectFile": "选择文件", + "basic": "基础", + "advanced": "高级", + "general": "通用", + "downloadDir": "下载目录", + "downloadDirValid": "请选择下载目录", + "connections": "连接数", + "items": "@count 条", + "subscribeTracker": "订阅 Tracker", + "subscribeFail": "订阅失败,请检查网络或稍后重试", + "update": "更新", + "updateDaily": "每天自动更新", + "lastUpdate": "上次更新:@time", + "addTracker": "添加 Tracker", + "addTrackerHit": "请输入 tracker 服务器地址,每行一条", + "ui": "界面", + "theme": "主题", + "themeSystem": "跟随系统", + "themeLight": "明亮主题", + "themeDark": "暗黑主题", + "locale": "语言", + "about": "关于", + "homepage": "主页", + "version": "版本", + "protocol": "通讯协议", + "port": "端口", + "apiToken": "接口令牌", + "notSet": "未设置", + "set": "已设置", + "effectAfterRestart": "此配置项将在重启应用后生效", + "deleteTask": "删除任务", + "deleteTaskTip": "保留已下载的文件", + "delete": "删除" +} diff --git a/ui/flutter/lib/app/data/api_provider.dart b/ui/flutter/lib/app/data/api_provider.dart new file mode 100644 index 00000000..d3683762 --- /dev/null +++ b/ui/flutter/lib/app/data/api_provider.dart @@ -0,0 +1,8 @@ +import 'package:get/get.dart'; + +class ApiProvider extends GetConnect { + @override + void onInit() { + httpClient.baseUrl = 'YOUR-API-URL'; + } +} diff --git a/ui/flutter/lib/app/modules/app/bindings/app_binding.dart b/ui/flutter/lib/app/modules/app/bindings/app_binding.dart new file mode 100644 index 00000000..af092f1f --- /dev/null +++ b/ui/flutter/lib/app/modules/app/bindings/app_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/app_controller.dart'; + +class AppBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => AppController(), + ); + } +} diff --git a/ui/flutter/lib/pages/app/app_controller.dart b/ui/flutter/lib/app/modules/app/controllers/app_controller.dart similarity index 96% rename from ui/flutter/lib/pages/app/app_controller.dart rename to ui/flutter/lib/app/modules/app/controllers/app_controller.dart index a83d2f1d..68d4191f 100644 --- a/ui/flutter/lib/pages/app/app_controller.dart +++ b/ui/flutter/lib/app/modules/app/controllers/app_controller.dart @@ -6,12 +6,12 @@ import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../api/api.dart'; -import '../../api/model/downloader_config.dart'; -import '../../core/common/start_config.dart'; -import '../../i18n/messages.dart'; -import '../../util/log_util.dart'; -import '../../util/util.dart'; +import '../../../../api/api.dart'; +import '../../../../api/model/downloader_config.dart'; +import '../../../../core/common/start_config.dart'; +import '../../../../i18n/messages.dart'; +import '../../../../util/log_util.dart'; +import '../../../../util/util.dart'; const _startConfigNetwork = "start.network"; const _startConfigAddress = "start.address"; diff --git a/ui/flutter/lib/pages/app/app_view.dart b/ui/flutter/lib/app/modules/app/views/app_view.dart similarity index 72% rename from ui/flutter/lib/pages/app/app_view.dart rename to ui/flutter/lib/app/modules/app/views/app_view.dart index 54c14cc5..fbaa5861 100644 --- a/ui/flutter/lib/pages/app/app_view.dart +++ b/ui/flutter/lib/app/modules/app/views/app_view.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; -import '../../i18n/messages.dart'; -import '../../routes/router.dart'; -import '../../theme/theme.dart'; -import '../home/home_controller.dart'; -import 'app_controller.dart'; +import '../../../../generated/locales.g.dart'; +import '../../../../i18n/messages.dart'; +import '../../../../theme/theme.dart'; +import '../../../routes/app_pages.dart'; +import '../controllers/app_controller.dart'; class AppView extends GetView { const AppView({Key? key}) : super(key: key); @@ -15,15 +15,13 @@ class AppView extends GetView { Widget build(BuildContext context) { final config = controller.downloaderConfig.value; return GetMaterialApp.router( - initialBinding: BindingsBuilder(() { - Get.lazyPut(() => HomeController(), fenix: true); - }), useInheritedMediaQuery: true, debugShowCheckedModeBanner: false, theme: GopeedTheme.light, darkTheme: GopeedTheme.dark, themeMode: ThemeMode.values.byName(config.extra.themeMode), - translations: messages, + // translations: messages, + translationsKeys: AppTranslation.translations, locale: toLocale(config.extra.locale), fallbackLocale: fallbackLocale, localizationsDelegates: const [ @@ -32,7 +30,7 @@ class AppView extends GetView { GlobalCupertinoLocalizations.delegate, ], supportedLocales: messages.keys.keys.map((e) => toLocale(e)).toList(), - getPages: Routes.routes, + getPages: AppPages.routes, ); } } diff --git a/ui/flutter/lib/app/modules/create/bindings/create_binding.dart b/ui/flutter/lib/app/modules/create/bindings/create_binding.dart new file mode 100644 index 00000000..b0cfabf9 --- /dev/null +++ b/ui/flutter/lib/app/modules/create/bindings/create_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/create_controller.dart'; + +class CreateBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => CreateController(), + ); + } +} diff --git a/ui/flutter/lib/pages/create/create_controller.dart b/ui/flutter/lib/app/modules/create/controllers/create_controller.dart similarity index 100% rename from ui/flutter/lib/pages/create/create_controller.dart rename to ui/flutter/lib/app/modules/create/controllers/create_controller.dart diff --git a/ui/flutter/lib/pages/create/create_view.dart b/ui/flutter/lib/app/modules/create/views/create_view.dart similarity index 92% rename from ui/flutter/lib/pages/create/create_view.dart rename to ui/flutter/lib/app/modules/create/views/create_view.dart index 94bf896c..9403cfd6 100644 --- a/ui/flutter/lib/pages/create/create_view.dart +++ b/ui/flutter/lib/app/modules/create/views/create_view.dart @@ -4,16 +4,16 @@ import 'package:get/get.dart'; import 'package:gopeed/api/model/resolve_result.dart'; import 'package:rounded_loading_button/rounded_loading_button.dart'; -import '../../api/api.dart'; -import '../../api/model/create_task.dart'; -import '../../api/model/options.dart'; -import '../../api/model/request.dart'; -import '../../routes/router.dart'; -import '../../util/util.dart'; -import '../../widget/directory_selector.dart'; -import '../../widget/file_list_view.dart'; -import '../app/app_controller.dart'; -import 'create_controller.dart'; +import '../../../../api/api.dart'; +import '../../../../api/model/create_task.dart'; +import '../../../../api/model/options.dart'; +import '../../../../api/model/request.dart'; +import '../../../../util/util.dart'; +import '../../../routes/app_pages.dart'; +import '../../../views/views/directory_selector.dart'; +import '../../../views/views/file_list_view.dart'; +import '../../app/controllers/app_controller.dart'; +import '../controllers/create_controller.dart'; class CreateView extends GetView { final _resolveFormKey = GlobalKey(); @@ -29,7 +29,7 @@ class CreateView extends GetView { appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back), - onPressed: () => Get.rootDelegate.offNamed(Routes.downloading)), + onPressed: () => Get.rootDelegate.offNamed(Routes.DOWNLOADING)), // actions: [], title: Text('create.title'.tr), ), @@ -185,7 +185,7 @@ class CreateView extends GetView { selectFiles: controller.selectedIndexs.cast()))); Get.back(); - Get.rootDelegate.offNamed(Routes.downloading); + Get.rootDelegate.offNamed(Routes.DOWNLOADING); } } catch (e) { Get.snackbar('error'.tr, e.toString()); diff --git a/ui/flutter/lib/app/modules/downloaded/bindings/downloaded_binding.dart b/ui/flutter/lib/app/modules/downloaded/bindings/downloaded_binding.dart new file mode 100644 index 00000000..a935e9ae --- /dev/null +++ b/ui/flutter/lib/app/modules/downloaded/bindings/downloaded_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/downloaded_controller.dart'; + +class DownloadedBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => DownloadedController(), + ); + } +} diff --git a/ui/flutter/lib/pages/downloaded/downloaded_controller.dart b/ui/flutter/lib/app/modules/downloaded/controllers/downloaded_controller.dart similarity index 88% rename from ui/flutter/lib/pages/downloaded/downloaded_controller.dart rename to ui/flutter/lib/app/modules/downloaded/controllers/downloaded_controller.dart index 87c10846..390aa510 100644 --- a/ui/flutter/lib/pages/downloaded/downloaded_controller.dart +++ b/ui/flutter/lib/app/modules/downloaded/controllers/downloaded_controller.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:get/get.dart'; -import '../../api/api.dart'; -import '../../api/model/task.dart'; +import '../../../../api/api.dart'; +import '../../../../api/model/task.dart'; class DownloadedController extends GetxController { final tasks = [].obs; diff --git a/ui/flutter/lib/pages/downloaded/downloaded_view.dart b/ui/flutter/lib/app/modules/downloaded/views/downloaded_view.dart similarity index 71% rename from ui/flutter/lib/pages/downloaded/downloaded_view.dart rename to ui/flutter/lib/app/modules/downloaded/views/downloaded_view.dart index 9f9144cc..d9d12e9e 100644 --- a/ui/flutter/lib/pages/downloaded/downloaded_view.dart +++ b/ui/flutter/lib/app/modules/downloaded/views/downloaded_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:gopeed/widget/buid_task_list_view.dart'; +import 'package:gopeed/app/views/views/buid_task_list_view.dart'; -import 'downloaded_controller.dart'; +import '../controllers/downloaded_controller.dart'; class DownloadedView extends GetView { const DownloadedView({Key? key}) : super(key: key); diff --git a/ui/flutter/lib/app/modules/downloading/bindings/downloading_binding.dart b/ui/flutter/lib/app/modules/downloading/bindings/downloading_binding.dart new file mode 100644 index 00000000..1be6275c --- /dev/null +++ b/ui/flutter/lib/app/modules/downloading/bindings/downloading_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/downloading_controller.dart'; + +class DownloadingBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => DownloadingController(), + ); + } +} diff --git a/ui/flutter/lib/pages/downloading/downloading_controller.dart b/ui/flutter/lib/app/modules/downloading/controllers/downloading_controller.dart similarity index 89% rename from ui/flutter/lib/pages/downloading/downloading_controller.dart rename to ui/flutter/lib/app/modules/downloading/controllers/downloading_controller.dart index f9c8bf0c..a6cf58b9 100644 --- a/ui/flutter/lib/pages/downloading/downloading_controller.dart +++ b/ui/flutter/lib/app/modules/downloading/controllers/downloading_controller.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:get/get.dart'; -import '../../api/api.dart'; -import '../../api/model/task.dart'; +import '../../../../api/api.dart'; +import '../../../../api/model/task.dart'; class DownloadingController extends GetxController { final tasks = [].obs; diff --git a/ui/flutter/lib/pages/downloading/downloading_view.dart b/ui/flutter/lib/app/modules/downloading/views/downloading_view.dart similarity index 73% rename from ui/flutter/lib/pages/downloading/downloading_view.dart rename to ui/flutter/lib/app/modules/downloading/views/downloading_view.dart index d861df08..6c6f6183 100644 --- a/ui/flutter/lib/pages/downloading/downloading_view.dart +++ b/ui/flutter/lib/app/modules/downloading/views/downloading_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../widget/buid_task_list_view.dart'; -import 'downloading_controller.dart'; +import '../../../views/views/buid_task_list_view.dart'; +import '../controllers/downloading_controller.dart'; class DownloadingView extends GetView { const DownloadingView({Key? key}) : super(key: key); diff --git a/ui/flutter/lib/app/modules/home/bindings/home_binding.dart b/ui/flutter/lib/app/modules/home/bindings/home_binding.dart new file mode 100644 index 00000000..79de3ae5 --- /dev/null +++ b/ui/flutter/lib/app/modules/home/bindings/home_binding.dart @@ -0,0 +1,13 @@ +import 'package:get/get.dart'; + +import '../controllers/home_controller.dart'; + +class HomeBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => HomeController(), + fenix: true, + ); + } +} diff --git a/ui/flutter/lib/pages/home/home_controller.dart b/ui/flutter/lib/app/modules/home/controllers/home_controller.dart similarity index 100% rename from ui/flutter/lib/pages/home/home_controller.dart rename to ui/flutter/lib/app/modules/home/controllers/home_controller.dart diff --git a/ui/flutter/lib/app/modules/home/views/home_view.dart b/ui/flutter/lib/app/modules/home/views/home_view.dart new file mode 100644 index 00000000..db3fc038 --- /dev/null +++ b/ui/flutter/lib/app/modules/home/views/home_view.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:stylish_bottom_bar/model/bar_items.dart'; +import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; + +import '../../../routes/app_pages.dart'; +import '../../../views/views/responsive_builder.dart'; +import '../controllers/home_controller.dart'; + +class HomeView extends GetView { + const HomeView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GetRouterOutlet.builder(builder: (context, delegate, currentRoute) { + return Scaffold( + extendBody: true, + body: Row( + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + !ResponsiveBuilder.isNarrow(context) + ? NavigationRail( + extended: true, + labelType: NavigationRailLabelType.none, + minExtendedWidth: 140, + groupAlignment: 0, + // useIndicator: false, + onDestinationSelected: (int index) { + switch (index) { + case 0: + delegate.toNamed(Routes.DOWNLOADING); + controller.currentIndex.value = 0; + break; + case 1: + delegate.toNamed(Routes.DOWNLOADED); + controller.currentIndex.value = 1; + break; + case 2: + delegate.toNamed(Routes.SETTING); + controller.currentIndex.value = 2; + break; + } + }, + destinations: [ + NavigationRailDestination( + icon: const Icon(Icons.format_align_left_outlined), + selectedIcon: + const Icon(Icons.format_align_left_outlined), + label: Text('downloading'.tr), + ), + NavigationRailDestination( + icon: const Icon(Icons.format_align_justify_outlined), + selectedIcon: + const Icon(Icons.format_align_justify_outlined), + label: Text('downloaded'.tr), + ), + NavigationRailDestination( + icon: const Icon(Icons.settings), + selectedIcon: const Icon(Icons.settings), + label: Text('setting'.tr), + ), + ], + selectedIndex: controller.currentIndex.value, + leading: const Icon(Icons.logo_dev), + // trailing: const Icon(Icons.info_outline), + ) + : const SizedBox.shrink(), + Expanded( + child: GetRouterOutlet( + initialRoute: Routes.DOWNLOADING, + // anchorRoute: '/', + // filterPages: (afterAnchor) { + // logger.w(afterAnchor); + // logger.w(afterAnchor.take(1)); + // return afterAnchor.take(1); + // }, + )) + ]), + bottomNavigationBar: ResponsiveBuilder.isNarrow(context) + ? StylishBottomBar( + option: AnimatedBarOptions( + iconSize: 32, + barAnimation: BarAnimation.blink, + iconStyle: IconStyle.Default, + opacity: 0.3, + ), + items: [ + BottomBarItem( + icon: const Icon(Icons.format_align_left_outlined), + selectedColor: Get.theme.primaryColor, + title: Text('downloading'.tr)), + BottomBarItem( + icon: const Icon(Icons.format_align_justify_outlined), + selectedColor: Get.theme.primaryColor, + title: Text('downloaded'.tr)), + BottomBarItem( + icon: const Icon(Icons.settings), + selectedColor: Get.theme.primaryColor, + title: Text('setting'.tr)), + ], + // hasNotch: true, + currentIndex: controller.currentIndex.value, + onTap: (index) { + switch (index) { + case 0: + delegate.toNamed(Routes.DOWNLOADING); + controller.currentIndex.value = 0; + break; + case 1: + delegate.toNamed(Routes.DOWNLOADED); + controller.currentIndex.value = 1; + break; + case 2: + delegate.toNamed(Routes.SETTING); + controller.currentIndex.value = 2; + break; + } + }, + ) + : const SizedBox.shrink(), + ); + }); + } +} diff --git a/ui/flutter/lib/app/modules/root/bindings/root_binding.dart b/ui/flutter/lib/app/modules/root/bindings/root_binding.dart new file mode 100644 index 00000000..2a106a1d --- /dev/null +++ b/ui/flutter/lib/app/modules/root/bindings/root_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import '../controllers/root_controller.dart'; + +class RootBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => RootController(), fenix: true); + } +} diff --git a/ui/flutter/lib/pages/root/root_controller.dart b/ui/flutter/lib/app/modules/root/controllers/root_controller.dart similarity index 73% rename from ui/flutter/lib/pages/root/root_controller.dart rename to ui/flutter/lib/app/modules/root/controllers/root_controller.dart index 6f4f9e0c..8a58a56c 100644 --- a/ui/flutter/lib/pages/root/root_controller.dart +++ b/ui/flutter/lib/app/modules/root/controllers/root_controller.dart @@ -1,4 +1,3 @@ import 'package:get/get.dart'; -import '../../api/api.dart'; class RootController extends GetxController {} diff --git a/ui/flutter/lib/pages/root/root_view.dart b/ui/flutter/lib/app/modules/root/views/root_view.dart similarity index 80% rename from ui/flutter/lib/pages/root/root_view.dart rename to ui/flutter/lib/app/modules/root/views/root_view.dart index 35d9fdbe..bf0b32b9 100644 --- a/ui/flutter/lib/pages/root/root_view.dart +++ b/ui/flutter/lib/app/modules/root/views/root_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../routes/router.dart'; -import 'root_controller.dart'; +import '../../../routes/app_pages.dart'; +import '../controllers/root_controller.dart'; class RootView extends GetView { const RootView({Key? key}) : super(key: key); @@ -12,7 +12,7 @@ class RootView extends GetView { return GetRouterOutlet.builder( builder: (context, delegate, current) { return GetRouterOutlet( - initialRoute: Routes.home, + initialRoute: Routes.HOME, // anchorRoute: '/', // filterPages: (afterAnchor) { // return afterAnchor.take(1); diff --git a/ui/flutter/lib/app/modules/setting/bindings/setting_binding.dart b/ui/flutter/lib/app/modules/setting/bindings/setting_binding.dart new file mode 100644 index 00000000..0d9494ae --- /dev/null +++ b/ui/flutter/lib/app/modules/setting/bindings/setting_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/setting_controller.dart'; + +class SettingBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => SettingController(), + ); + } +} diff --git a/ui/flutter/lib/pages/setting/setting_controller.dart b/ui/flutter/lib/app/modules/setting/controllers/setting_controller.dart similarity index 100% rename from ui/flutter/lib/pages/setting/setting_controller.dart rename to ui/flutter/lib/app/modules/setting/controllers/setting_controller.dart diff --git a/ui/flutter/lib/pages/setting/setting_view.dart b/ui/flutter/lib/app/modules/setting/views/setting_view.dart similarity index 89% rename from ui/flutter/lib/pages/setting/setting_view.dart rename to ui/flutter/lib/app/modules/setting/views/setting_view.dart index 3d293721..c02ec9f2 100644 --- a/ui/flutter/lib/pages/setting/setting_view.dart +++ b/ui/flutter/lib/app/modules/setting/views/setting_view.dart @@ -7,14 +7,14 @@ import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../../i18n/messages.dart'; -import '../../util/package_info.dart'; -import '../../util/util.dart'; -import '../../widget/check_list_view.dart'; -import '../../widget/directory_selector.dart'; -import '../../widget/outlined_button_loading.dart'; -import '../app/app_controller.dart'; -import 'setting_controller.dart'; +import '../../../../i18n/messages.dart'; +import '../../../../util/package_info.dart'; +import '../../../../util/util.dart'; +import '../../../views/views/check_list_view.dart'; +import '../../../views/views/directory_selector.dart'; +import '../../../views/views/outlined_button_loading.dart'; +import '../../app/controllers/app_controller.dart'; +import '../controllers/setting_controller.dart'; const _padding = SizedBox(height: 10); final _divider = const Divider().paddingOnly(left: 10, right: 10); @@ -38,7 +38,7 @@ class SettingView extends GetView { .then(completer.complete) .onError(completer.completeError); if (needRestart) { - Get.snackbar('tip'.tr, 'setting.effectAfterRestart'.tr); + Get.snackbar('tip'.tr, 'effectAfterRestart'.tr); } }); return completer.future; @@ -46,8 +46,7 @@ class SettingView extends GetView { // download basic config items start final buildDownloadDir = _buildConfigItem( - 'setting.downloadDir', () => downloaderCfg.value.downloadDir, - (Key key) { + 'downloadDir', () => downloaderCfg.value.downloadDir, (Key key) { final downloadDirController = TextEditingController(text: downloaderCfg.value.downloadDir); downloadDirController.addListener(() async { @@ -69,8 +68,7 @@ class SettingView extends GetView { // http config items start final httpConfig = downloaderCfg.value.protocolConfig.http; final buildHttpConnections = _buildConfigItem( - 'setting.connections'.tr, () => httpConfig.connections.toString(), - (Key key) { + 'connections'.tr, () => httpConfig.connections.toString(), (Key key) { final connectionsController = TextEditingController(text: httpConfig.connections.toString()); connectionsController.addListener(() async { @@ -98,8 +96,8 @@ class SettingView extends GetView { final btExtConfig = downloaderCfg.value.extra.bt; final buildBtTrackerSubscribeUrls = _buildConfigItem( - 'setting.subscribeTracker'.tr, - () => 'setting.items'.trParams( + 'subscribeTracker'.tr, + () => 'items'.trParams( {'count': btExtConfig.trackerSubscribeUrls.length.toString()}), (Key key) { final trackerUpdateController = OutlinedButtonLoadingController(); @@ -127,24 +125,24 @@ class SettingView extends GetView { try { await appController.trackerUpdate(); } catch (e) { - Get.snackbar('error'.tr, 'setting.subscribeFail'.tr); + Get.snackbar('error'.tr, 'subscribeFail'.tr); } finally { trackerUpdateController.stop(); } }, controller: trackerUpdateController, - child: Text('setting.update'.tr), + child: Text('update'.tr), ), Expanded( child: SwitchListTile( controlAffinity: ListTileControlAffinity.leading, value: true, onChanged: (bool value) {}, - title: Text('setting.updateDaily'.tr)), + title: Text('updateDaily'.tr)), ), ], ), - Text('setting.lastUpdate'.trParams({ + Text('lastUpdate'.trParams({ 'time': btExtConfig.lastTrackerUpdateTime != null ? DateFormat('yyyy-MM-dd HH:mm:ss') .format(btExtConfig.lastTrackerUpdateTime!) @@ -154,8 +152,8 @@ class SettingView extends GetView { ); }); final buildBtTrackers = _buildConfigItem( - 'setting.addTracker'.tr, - () => 'setting.items' + 'addTracker'.tr, + () => 'items' .trParams({'count': btExtConfig.customTrackers.length.toString()}), (Key key) { final trackersController = TextEditingController( @@ -168,7 +166,7 @@ class SettingView extends GetView { keyboardType: TextInputType.multiline, maxLines: 5, decoration: InputDecoration( - hintText: 'setting.addTrackerHit'.tr, + hintText: 'addTrackerHit'.tr, ), onChanged: (value) async { btExtConfig.customTrackers = ls.convert(value); @@ -181,7 +179,7 @@ class SettingView extends GetView { // ui config items start final buildTheme = _buildConfigItem( - 'setting.theme', + 'theme', () => _getThemeName(downloaderCfg.value.extra.themeMode), (Key key) => DropdownButton( key: key, @@ -203,7 +201,7 @@ class SettingView extends GetView { .toList(), )); final buildLocale = _buildConfigItem( - 'setting.locale', + 'locale', () => messages.keys[downloaderCfg.value.extra.locale]!['label']!, (Key key) => DropdownButton( key: key, @@ -228,7 +226,7 @@ class SettingView extends GetView { // about config items start buildHomepage() => ListTile( - title: Text('setting.homepage'.tr), + title: Text('homepage'.tr), subtitle: const Text('https://github.com/GopeedLab/gopeed'), onTap: () { launchUrl(Uri.parse('https://github.com/GopeedLab/gopeed'), @@ -236,13 +234,13 @@ class SettingView extends GetView { }, ); buildVersion() => ListTile( - title: Text('setting.version'.tr), + title: Text('version'.tr), subtitle: Text(packageInfo.version), ); // advanced config items start final buildApiProtocol = _buildConfigItem( - 'setting.protocol'.tr, + 'protocol'.tr, () => startCfg.value.network == 'tcp' ? 'TCP ${startCfg.value.address}' : 'Unix', @@ -320,7 +318,7 @@ class SettingView extends GetView { child: TextFormField( controller: portController, decoration: InputDecoration( - labelText: 'setting.port'.tr, + labelText: 'port'.tr, contentPadding: const EdgeInsets.all(0.0), ), keyboardType: TextInputType.number, @@ -340,11 +338,9 @@ class SettingView extends GetView { ); }, ); - final buildApiToken = _buildConfigItem( - 'setting.apiToken'.tr, - () => startCfg.value.apiToken.isEmpty - ? 'setting.notSet'.tr - : 'setting.seted'.tr, (Key key) { + final buildApiToken = _buildConfigItem('apiToken'.tr, + () => startCfg.value.apiToken.isEmpty ? 'notSet'.tr : 'seted'.tr, + (Key key) { final apiTokenController = TextEditingController(text: startCfg.value.apiToken); apiTokenController.addListener(() async { @@ -377,10 +373,10 @@ class SettingView extends GetView { bottom: TabBar( tabs: [ Tab( - text: 'setting.basic'.tr, + text: 'basic'.tr, ), Tab( - text: 'setting.advanced'.tr, + text: 'advanced'.tr, ), ], ), @@ -391,7 +387,7 @@ class SettingView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: _addPadding([ - Text('setting.general'.tr), + Text('general'.tr), Card( child: Column( children: _addDivider([ @@ -413,7 +409,7 @@ class SettingView extends GetView { buildBtTrackers(), ]), )), - Text('setting.ui'.tr), + Text('ui'.tr), Card( child: Column( children: _addDivider([ @@ -421,7 +417,7 @@ class SettingView extends GetView { buildLocale(), ]), )), - Text('setting.about'.tr), + Text('about'.tr), Card( child: Column( children: _addDivider([ @@ -519,22 +515,22 @@ class SettingView extends GetView { String _getThemeName(String? themeMode) { switch (ThemeMode.values.byName(themeMode ?? ThemeMode.system.name)) { case ThemeMode.light: - return 'setting.themeLight'.tr; + return 'themeLight'.tr; case ThemeMode.dark: - return 'setting.themeDark'.tr; + return 'themeDark'.tr; default: - return 'setting.themeSystem'.tr; + return 'themeSystem'.tr; } } } -class _ConfigItem { - late String label; - late String Function() text; - late Widget Function(Key key) inputItem; - - _ConfigItem(this.label, this.text, this.inputItem); -} +// class _ConfigItem { +// late String label; +// late String Function() text; +// late Widget Function(Key key) inputItem; +// +// _ConfigItem(this.label, this.text, this.inputItem); +// } class _NumericalRangeFormatter extends TextInputFormatter { final int min; diff --git a/ui/flutter/lib/app/routes/app_pages.dart b/ui/flutter/lib/app/routes/app_pages.dart new file mode 100644 index 00000000..7b619cc0 --- /dev/null +++ b/ui/flutter/lib/app/routes/app_pages.dart @@ -0,0 +1,68 @@ +import 'package:get/get.dart'; + +import '../modules/create/bindings/create_binding.dart'; +import '../modules/create/views/create_view.dart'; +import '../modules/downloaded/bindings/downloaded_binding.dart'; +import '../modules/downloaded/views/downloaded_view.dart'; +import '../modules/downloading/bindings/downloading_binding.dart'; +import '../modules/downloading/views/downloading_view.dart'; +import '../modules/home/bindings/home_binding.dart'; +import '../modules/home/views/home_view.dart'; +import '../modules/root/bindings/root_binding.dart'; +import '../modules/root/views/root_view.dart'; +import '../modules/setting/bindings/setting_binding.dart'; +import '../modules/setting/views/setting_view.dart'; + +part 'app_routes.dart'; + +class AppPages { + AppPages._(); + + static const INITIAL = Routes.ROOT; + + static final routes = [ + GetPage( + name: _Paths.ROOT, + participatesInRootNavigator: true, + transition: Transition.topLevel, + // preventDuplicates: true, + page: () => const RootView(), + binding: RootBinding(), + children: [ + GetPage( + name: _Paths.HOME, + // participatesInRootNavigator: true, + // transition: Transition.topLevel, + // preventDuplicates: true, + page: () => const HomeView(), + binding: HomeBinding(), + children: [ + GetPage( + name: _Paths.DOWNLOADED, + transition: Transition.noTransition, + page: () => const DownloadedView(), + binding: DownloadedBinding(), + ), + GetPage( + name: _Paths.DOWNLOADING, + transition: Transition.noTransition, + page: () => const DownloadingView(), + binding: DownloadingBinding(), + ), + GetPage( + name: _Paths.SETTING, + transition: Transition.noTransition, + page: () => const SettingView(), + binding: SettingBinding(), + ), + ]), + GetPage( + name: _Paths.CREATE, + transition: Transition.downToUp, + // preventDuplicates: true, + page: () => CreateView(), + binding: CreateBinding(), + ), + ]), + ]; +} diff --git a/ui/flutter/lib/app/routes/app_routes.dart b/ui/flutter/lib/app/routes/app_routes.dart new file mode 100644 index 00000000..ab00a934 --- /dev/null +++ b/ui/flutter/lib/app/routes/app_routes.dart @@ -0,0 +1,22 @@ +part of 'app_pages.dart'; +// DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart + +abstract class Routes { + Routes._(); + static const DOWNLOADED = _Paths.HOME + _Paths.DOWNLOADED; + static const DOWNLOADING = _Paths.HOME + _Paths.DOWNLOADING; + static const CREATE = _Paths.CREATE; + static const ROOT = _Paths.ROOT; + static const SETTING = _Paths.HOME + _Paths.SETTING; + static const HOME = _Paths.HOME; +} + +abstract class _Paths { + _Paths._(); + static const DOWNLOADED = '/downloaded'; + static const DOWNLOADING = '/downloading'; + static const CREATE = '/create'; + static const ROOT = '/'; + static const SETTING = '/setting'; + static const HOME = '/home'; +} diff --git a/ui/flutter/lib/widget/buid_task_list_view.dart b/ui/flutter/lib/app/views/views/buid_task_list_view.dart similarity index 96% rename from ui/flutter/lib/widget/buid_task_list_view.dart rename to ui/flutter/lib/app/views/views/buid_task_list_view.dart index 2cd9af03..3c4c81c9 100644 --- a/ui/flutter/lib/widget/buid_task_list_view.dart +++ b/ui/flutter/lib/app/views/views/buid_task_list_view.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../../api/api.dart'; -import '../../api/model/task.dart'; -import '../../routes/router.dart'; -import '../../util/util.dart'; +import '../../../api/api.dart'; +import '../../../api/model/task.dart'; +import '../../../util/util.dart'; +import '../../routes/app_pages.dart'; class BuildTaskListView extends GetView { final List tasks; @@ -26,7 +26,7 @@ class BuildTaskListView extends GetView { icon: const Icon(Icons.add), tooltip: 'create.title'.tr, onPressed: () { - Get.rootDelegate.toNamed(Routes.create); + Get.rootDelegate.toNamed(Routes.CREATE); }, ), //TODO appBar toggleALl/start selected/delete selected/ diff --git a/ui/flutter/lib/widget/check_list_view.dart b/ui/flutter/lib/app/views/views/check_list_view.dart similarity index 100% rename from ui/flutter/lib/widget/check_list_view.dart rename to ui/flutter/lib/app/views/views/check_list_view.dart diff --git a/ui/flutter/lib/widget/directory_selector.dart b/ui/flutter/lib/app/views/views/directory_selector.dart similarity index 87% rename from ui/flutter/lib/widget/directory_selector.dart rename to ui/flutter/lib/app/views/views/directory_selector.dart index edf4ce5c..e2976746 100644 --- a/ui/flutter/lib/widget/directory_selector.dart +++ b/ui/flutter/lib/app/views/views/directory_selector.dart @@ -2,8 +2,8 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../util/mac_secure_util.dart'; -import '../util/util.dart'; +import '../../../util/mac_secure_util.dart'; +import '../../../util/util.dart'; class DirectorySelector extends StatefulWidget { final TextEditingController controller; @@ -28,11 +28,11 @@ class _DirectorySelectorState extends State { controller: widget.controller, decoration: widget.showLabel ? InputDecoration( - labelText: 'setting.downloadDir'.tr, + labelText: 'downloadDir'.tr, ) : null, validator: (v) { - return v!.trim().isNotEmpty ? null : 'setting.downloadDirValid'.tr; + return v!.trim().isNotEmpty ? null : 'downloadDirValid'.tr; }, )), !Util.isDesktop() diff --git a/ui/flutter/lib/widget/file_list_view.dart b/ui/flutter/lib/app/views/views/file_list_view.dart similarity index 96% rename from ui/flutter/lib/widget/file_list_view.dart rename to ui/flutter/lib/app/views/views/file_list_view.dart index 4556681e..46c82f1c 100644 --- a/ui/flutter/lib/widget/file_list_view.dart +++ b/ui/flutter/lib/app/views/views/file_list_view.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:path/path.dart' as path; -import '../pages/app/app_controller.dart'; -import '../pages/create/create_controller.dart'; -import '../util/file_icon.dart'; -import '../util/util.dart'; +import '../../../util/file_icon.dart'; +import '../../../util/util.dart'; +import '../../modules/app/controllers/app_controller.dart'; +import '../../modules/create/controllers/create_controller.dart'; class FileListView extends GetView { FileListView({ diff --git a/ui/flutter/lib/widget/outlined_button_loading.dart b/ui/flutter/lib/app/views/views/outlined_button_loading.dart similarity index 100% rename from ui/flutter/lib/widget/outlined_button_loading.dart rename to ui/flutter/lib/app/views/views/outlined_button_loading.dart diff --git a/ui/flutter/lib/app/views/views/responsive_builder.dart b/ui/flutter/lib/app/views/views/responsive_builder.dart new file mode 100644 index 00000000..0cf4ffd6 --- /dev/null +++ b/ui/flutter/lib/app/views/views/responsive_builder.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class ResponsiveBuilder extends StatelessWidget { + const ResponsiveBuilder({ + required this.narrowBuilder, + required this.mediumBuilder, + required this.wideBuilder, + Key? key, + }) : super(key: key); + + final Widget Function( + BuildContext context, + BoxConstraints constraints, + ) narrowBuilder; + + final Widget Function( + BuildContext context, + BoxConstraints constraints, + ) mediumBuilder; + + final Widget Function( + BuildContext context, + BoxConstraints constraints, + ) wideBuilder; + + static bool isNarrow(BuildContext context) => + MediaQuery.of(context).size.width < 768; + + static bool isMedium(BuildContext context) => + MediaQuery.of(context).size.width < 992 && + MediaQuery.of(context).size.width >= 768; + + static bool isWide(BuildContext context) => + MediaQuery.of(context).size.width >= 992; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth >= 992) { + return wideBuilder(context, constraints); + } else if (constraints.maxWidth >= 768) { + return mediumBuilder(context, constraints); + } else { + return narrowBuilder(context, constraints); + } + }, + ); + } +} diff --git a/ui/flutter/lib/widget/task_list_view.dart b/ui/flutter/lib/app/views/views/task_list_view.dart similarity index 100% rename from ui/flutter/lib/widget/task_list_view.dart rename to ui/flutter/lib/app/views/views/task_list_view.dart diff --git a/ui/flutter/lib/generated/locales.g.dart b/ui/flutter/lib/generated/locales.g.dart new file mode 100644 index 00000000..b09b88cc --- /dev/null +++ b/ui/flutter/lib/generated/locales.g.dart @@ -0,0 +1,284 @@ +// DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart + +// ignore_for_file: lines_longer_than_80_chars +// ignore: avoid_classes_with_only_static_members +class AppTranslation { + static Map> translations = { + 'en_US': Locales.en_US, + 'fa_IR': Locales.fa_IR, + 'ru_RU': Locales.ru_RU, + 'zh_CN': Locales.zh_CN, + }; +} + +class LocaleKeys { + LocaleKeys._(); + static const label = 'label'; + static const error = 'error'; + static const tip = 'tip'; + static const confirm = 'confirm'; + static const cancel = 'cancel'; + static const selectAll = 'selectAll'; + static const task = 'task'; + static const downloading = 'downloading'; + static const downloaded = 'downloaded'; + static const setting = 'setting'; + static const create = 'create'; + static const downloadLink = 'downloadLink'; + static const downloadLinkValid = 'downloadLinkValid'; + static const downloadLinkHit = 'downloadLinkHit'; + static const downloadLinkHitDesktop = 'downloadLinkHitDesktop'; + static const download = 'download'; + static const noStoragePermission = 'noStoragePermission'; + static const selectFile = 'selectFile'; + static const basic = 'basic'; + static const advanced = 'advanced'; + static const general = 'general'; + static const downloadDir = 'downloadDir'; + static const downloadDirValid = 'downloadDirValid'; + static const connections = 'connections'; + static const items = 'items'; + static const subscribeTracker = 'subscribeTracker'; + static const subscribeFail = 'subscribeFail'; + static const update = 'update'; + static const updateDaily = 'updateDaily'; + static const lastUpdate = 'lastUpdate'; + static const addTracker = 'addTracker'; + static const addTrackerHit = 'addTrackerHit'; + static const ui = 'ui'; + static const theme = 'theme'; + static const themeSystem = 'themeSystem'; + static const themeLight = 'themeLight'; + static const themeDark = 'themeDark'; + static const locale = 'locale'; + static const about = 'about'; + static const homepage = 'homepage'; + static const version = 'version'; + static const protocol = 'protocol'; + static const port = 'port'; + static const apiToken = 'apiToken'; + static const notSet = 'notSet'; + static const set = 'set'; + static const effectAfterRestart = 'effectAfterRestart'; + static const deleteTask = 'deleteTask'; + static const deleteTaskTip = 'deleteTaskTip'; + static const delete = 'delete'; + static const title = 'title'; +} + +class Locales { + static const en_US = { + 'label': 'English', + 'error': 'Error', + 'tip': 'Tip', + 'confirm': 'Confirm', + 'cancel': 'Cancel', + 'selectAll': 'Select All', + 'task': 'Task', + 'downloading': 'downloading', + 'downloaded': 'downloaded', + 'setting': 'Setting', + 'create': 'Create Task', + 'downloadLink': 'Download Link', + 'downloadLinkValid': 'Please enter the download link', + 'downloadLinkHit': + 'Please enter the download link, HTTP/HTTPS/MAGNET supported@append', + 'downloadLinkHitDesktop': ', or drag the torrent file here directly', + 'download': 'Download', + 'noStoragePermission': 'Storage permission required', + 'selectFile': 'Select File', + 'basic': 'Basic', + 'advanced': 'Advanced', + 'general': 'General', + 'downloadDir': 'Download Directory', + 'downloadDirValid': 'Please select the download directory', + 'connections': 'Connections', + 'items': '@count items', + 'subscribeTracker': 'Subscribe Tracker', + 'subscribeFail': + 'Subscribe failed, please check network or try again later', + 'update': 'Update', + 'updateDaily': 'Update daily', + 'lastUpdate': 'Last update: @time', + 'addTracker': 'Add Tracker', + 'addTrackerHit': 'Please enter the tracker server url, one per line', + 'ui': 'UI', + 'theme': 'Theme', + 'themeSystem': 'System', + 'themeLight': 'Light', + 'themeDark': 'Dark', + 'locale': 'Language', + 'about': 'About', + 'homepage': 'Homepage', + 'version': 'Version', + 'protocol': 'Protocol', + 'port': 'Port', + 'apiToken': 'API Token', + 'notSet': 'NS', + 'set': 'SET', + 'effectAfterRestart': 'Effect after restart', + 'deleteTask': 'Delete Task', + 'deleteTaskTip': 'Keep downloaded files', + 'delete': 'Delete', + }; + static const fa_IR = { + 'label': 'فارسی', + 'error': 'اررور', + 'tip': 'Tip', + 'confirm': 'تایید', + 'cancel': 'انصراف', + 'selectAll': 'انتخاب همه', + 'task': 'کار', + 'downloading': 'downloading', + 'downloaded': 'downloaded', + 'setting': 'تنظیمات', + 'title': 'ایجاد کار', + 'downloadLink': 'لینک دانلود', + 'downloadLinkValid': 'لطفا لینک دانلود را وارد کنید', + 'downloadLinkHit': + 'Please enter the download link, HTTP/HTTPS/MAGNET supported@append', + 'downloadLinkHitDesktop': ', or drag the torrent file here directly', + 'download': 'دانلود', + 'noStoragePermission': 'مجوز ذخیره سازی مورد نیاز است', + 'selectFile': 'انتخاب فایل', + 'basic': 'پایه', + 'advanced': 'پیشرفته', + 'general': 'عمومی', + 'downloadDir': 'دایرکتوری دانلود', + 'downloadDirValid': 'لطفا دایرکتوری دانلود را انتخاب کنید', + 'connections': 'اتصالات', + 'items': '@count آیتم ها', + 'subscribeTracker': 'Subscribe Tracker', + 'subscribeFail': + 'Subscribe failed, please check network or try again later', + 'update': 'بروزرسانی', + 'updateDaily': 'بروزرسانی روزانه', + 'lastUpdate': 'Last update: @time', + 'addTracker': 'Add Tracker', + 'addTrackerHit': 'Please enter the tracker server url, one per line', + 'ui': 'رابط کاربری', + 'theme': 'پوسته', + 'themeSystem': 'سیستم', + 'themeLight': 'روشن', + 'themeDark': 'تاریک', + 'locale': 'Language', + 'about': 'درباره ی', + 'homepage': 'صفحه اصلی', + 'version': 'نسخه', + 'protocol': 'پروتکل', + 'port': 'پورت', + 'apiToken': 'API توکن', + 'notSet': 'NS', + 'set': 'SET', + 'effectAfterRestart': 'Effect after restart', + 'deleteTask': 'پاک کردن کار', + 'deleteTaskTip': 'فایل های دانلود شده را نگه دارد', + 'delete': 'پاک کردن', + }; + static const ru_RU = { + 'label': 'Русский(Россия)', + 'error': 'Ошибка', + 'tip': 'Подсказка', + 'confirm': 'Подтвердить', + 'cancel': 'Отмена', + 'selectAll': 'Выбрать все', + 'task': 'Задачи', + 'downloading': 'downloading', + 'downloaded': 'downloaded', + 'setting': 'Настройки', + 'title': 'Создать задачу', + 'downloadLink': 'Ссылка для скачивание', + 'downloadLinkValid': 'Пожалуйста, введите ссылку для скачивания', + 'downloadLinkHit': + 'Пожалуйста, введите ссылку для скачивания, HTTP/HTTPS/MAGNET supported@append', + 'downloadLinkHitDesktop': ', или перетащите сюда торрент-файл', + 'download': 'Скачать', + 'noStoragePermission': 'Требуется доступ к хранилищу', + 'selectFile': 'Выберать файл', + 'basic': 'Основные параметры', + 'advanced': 'Расширенные', + 'general': 'Общие', + 'downloadDir': 'Папка загрузки', + 'downloadDirValid': 'Пожалуйста, выберите папку для загрузки', + 'connections': 'Соединений', + 'items': '@подсчет элементов', + 'subscribeTracker': 'Подпись трекера', + 'subscribeFail': + 'Подписка не удалась, пожалуйста, проверьте сеть или повторите попытку позже', + 'update': 'Обновить', + 'updateDaily': 'Обновлять ежедневно', + 'lastUpdate': 'Последнее обновление: @время', + 'addTracker': 'Добавить трекер', + 'addTrackerHit': 'Пожалуйста, введите url трекера, по одному в строке', + 'ui': 'UI', + 'theme': 'Тема', + 'themeSystem': 'Системная', + 'themeLight': 'Светлая', + 'themeDark': 'Темная', + 'locale': 'Язык', + 'about': 'О приложении', + 'homepage': 'Домашняя страница', + 'version': 'Версия', + 'protocol': 'Протокол', + 'port': 'Порт', + 'apiToken': 'API Токен', + 'notSet': 'Не установлено', + 'set': 'Установлено', + 'effectAfterRestart': 'Эффект после перезагрузки', + 'deleteTask': 'Удалить задачу', + 'deleteTaskTip': 'Сохранить загруженные файлы', + 'delete': 'Удалить', + }; + static const zh_CN = { + 'label': '中文(简体)', + 'error': '错误', + 'tip': '提示', + 'confirm': '确认', + 'cancel': '取消', + 'selectAll': '全选', + 'task': '任务', + 'downloading': '下载中', + 'downloaded': '已完成', + 'setting': '设置', + 'title': '创建任务', + 'downloadLink': '下载链接', + 'downloadLinkValid': '请输入下载链接', + 'downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append', + 'downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处', + 'download': '下载', + 'noStoragePermission': '需要开启存储权限', + 'selectFile': '选择文件', + 'basic': '基础', + 'advanced': '高级', + 'general': '通用', + 'downloadDir': '下载目录', + 'downloadDirValid': '请选择下载目录', + 'connections': '连接数', + 'items': '@count 条', + 'subscribeTracker': '订阅 Tracker', + 'subscribeFail': '订阅失败,请检查网络或稍后重试', + 'update': '更新', + 'updateDaily': '每天自动更新', + 'lastUpdate': '上次更新:@time', + 'addTracker': '添加 Tracker', + 'addTrackerHit': '请输入 tracker 服务器地址,每行一条', + 'ui': '界面', + 'theme': '主题', + 'themeSystem': '跟随系统', + 'themeLight': '明亮主题', + 'themeDark': '暗黑主题', + 'locale': '语言', + 'about': '关于', + 'homepage': '主页', + 'version': '版本', + 'protocol': '通讯协议', + 'port': '端口', + 'apiToken': '接口令牌', + 'notSet': '未设置', + 'set': '已设置', + 'effectAfterRestart': '此配置项将在重启应用后生效', + 'deleteTask': '删除任务', + 'deleteTaskTip': '保留已下载的文件', + 'delete': '删除', + }; +} diff --git a/ui/flutter/lib/main.dart b/ui/flutter/lib/main.dart index 538c5256..97abae13 100644 --- a/ui/flutter/lib/main.dart +++ b/ui/flutter/lib/main.dart @@ -4,9 +4,9 @@ import 'package:get/get.dart'; import 'package:gopeed/api/api.dart' as api; import 'package:gopeed/i18n/messages.dart'; +import 'app/modules/app/controllers/app_controller.dart'; +import 'app/modules/app/views/app_view.dart'; import 'core/libgopeed_boot.dart'; -import 'pages/app/app_controller.dart'; -import 'pages/app/app_view.dart'; import 'util/log_util.dart'; import 'util/mac_secure_util.dart'; import 'util/package_info.dart'; diff --git a/ui/flutter/lib/pages/home/home_view.dart b/ui/flutter/lib/pages/home/home_view.dart deleted file mode 100644 index acbf14dd..00000000 --- a/ui/flutter/lib/pages/home/home_view.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:stylish_bottom_bar/model/bar_items.dart'; -import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; - -import '../../routes/router.dart'; -import '../../util/responsive_builder.dart'; -import 'home_controller.dart'; - -class HomeView extends GetView { - const HomeView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return GetRouterOutlet.builder( - builder: (context, delegate, currentRoute) { - //This router outlet handles the appbar and the bottom navigation bar - return Scaffold( - extendBody: true, - body: Row( - // crossAxisAlignment: CrossAxisAlignment.start, - children: [ - !ResponsiveBuilder.isNarrow(context) - ? NavigationRail( - extended: true, - labelType: NavigationRailLabelType.none, - minExtendedWidth: 140, - groupAlignment: 0, - // useIndicator: false, - onDestinationSelected: (int index) { - switch (index) { - case 0: - delegate.toNamed(Routes.downloading); - controller.currentIndex.value = 0; - break; - case 1: - delegate.toNamed(Routes.downloaded); - controller.currentIndex.value = 1; - break; - case 2: - delegate.toNamed(Routes.setting); - controller.currentIndex.value = 2; - break; - } - }, - destinations: [ - NavigationRailDestination( - icon: const Icon(Icons.format_align_left_outlined), - selectedIcon: - const Icon(Icons.format_align_left_outlined), - label: Text('home.downloading'.tr), - ), - NavigationRailDestination( - icon: - const Icon(Icons.format_align_justify_outlined), - selectedIcon: - const Icon(Icons.format_align_justify_outlined), - label: Text('home.downloaded'.tr), - ), - NavigationRailDestination( - icon: const Icon(Icons.settings), - selectedIcon: const Icon(Icons.settings), - label: Text('home.setting'.tr), - ), - ], - selectedIndex: controller.currentIndex.value, - leading: const Icon(Icons.logo_dev), - // trailing: const Icon(Icons.info_outline), - ) - : const SizedBox.shrink(), - Expanded( - child: GetRouterOutlet( - initialRoute: Routes.downloading, - )) - ]), - bottomNavigationBar: ResponsiveBuilder.isNarrow(context) - ? StylishBottomBar( - option: AnimatedBarOptions( - iconSize: 32, - barAnimation: BarAnimation.blink, - iconStyle: IconStyle.Default, - opacity: 0.3, - ), - items: [ - BottomBarItem( - icon: const Icon(Icons.format_align_left_outlined), - selectedColor: Get.theme.primaryColor, - title: Text('home.downloading'.tr)), - BottomBarItem( - icon: const Icon(Icons.format_align_justify_outlined), - selectedColor: Get.theme.primaryColor, - title: Text('home.downloaded'.tr)), - BottomBarItem( - icon: const Icon(Icons.settings), - selectedColor: Get.theme.primaryColor, - title: Text('home.setting'.tr)), - ], - // hasNotch: true, - currentIndex: controller.currentIndex.value, - onTap: (index) { - switch (index) { - case 0: - delegate.toNamed(Routes.downloading); - controller.currentIndex.value = 0; - break; - case 1: - delegate.toNamed(Routes.downloaded); - controller.currentIndex.value = 1; - break; - case 2: - delegate.toNamed(Routes.setting); - controller.currentIndex.value = 2; - break; - } - }, - ) - : const SizedBox.shrink(), - // floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling, - // floatingActionButton: ResponsiveBuilder.isNarrow(context) - // ? FloatingActionButton( - // onPressed: () { - // /* Get.isDarkMode - // ? { - // Get.changeTheme(GopeedTheme.light), - // Get.changeThemeMode(ThemeMode.light) - // } - // : { - // Get.changeTheme(GopeedTheme.dark), - // Get.changeThemeMode(ThemeMode.dark) - // }; */ - // Get.rootDelegate.toNamed(Routes.create); - // }, - // child: const Icon( - // Icons.add, - // )) - // : null, - // floatingActionButtonLocation: - // FloatingActionButtonLocation.centerDocked, - ); - }, - ); - } -} diff --git a/ui/flutter/lib/routes/router.dart b/ui/flutter/lib/routes/router.dart deleted file mode 100644 index eae3944d..00000000 --- a/ui/flutter/lib/routes/router.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:get/get.dart'; - -import '../pages/create/create_controller.dart'; -import '../pages/create/create_view.dart'; -import '../pages/downloaded/downloaded_controller.dart'; -import '../pages/downloaded/downloaded_view.dart'; -import '../pages/downloading/downloading_controller.dart'; -import '../pages/downloading/downloading_view.dart'; -import '../pages/home/home_controller.dart'; -import '../pages/home/home_view.dart'; -import '../pages/root/root_controller.dart'; -import '../pages/root/root_view.dart'; -import '../pages/setting/setting_controller.dart'; -import '../pages/setting/setting_view.dart'; - -abstract class _Paths { - static const root = '/'; - - static const create = '/create'; - - static const home = '/home'; - static const downloading = '/downloading'; - static const downloaded = '/downloaded'; - static const setting = '/setting'; -} - -class Routes { - static const root = _Paths.root; - - static const create = _Paths.create; - - static const home = _Paths.home; - static const downloading = _Paths.home + _Paths.downloading; - static const downloaded = _Paths.home + _Paths.downloaded; - - static const setting = _Paths.home + _Paths.setting; - - static final routes = [ - GetPage( - name: _Paths.root, - participatesInRootNavigator: true, - transition: Transition.topLevel, - // preventDuplicates: true, - page: () => const RootView(), - binding: BindingsBuilder(() { - Get.lazyPut(() => RootController(), fenix: true); - }), - children: [ - GetPage( - name: _Paths.create, - transition: Transition.downToUp, - // preventDuplicates: true, - page: () => CreateView(), - binding: BindingsBuilder(() { - Get.lazyPut(() => CreateController(), - fenix: true); - }), - ), - GetPage( - name: _Paths.home, - // transition: Transition.upToDown, - // preventDuplicates: true, - page: () => const HomeView(), - binding: BindingsBuilder(() { - Get.lazyPut(() => HomeController(), - fenix: true); - }), - children: [ - GetPage( - name: _Paths.downloading, - transition: Transition.noTransition, - page: () => const DownloadingView(), - binding: BindingsBuilder(() { - Get.lazyPut( - () => DownloadingController(), - ); - }), - ), - GetPage( - name: _Paths.downloaded, - transition: Transition.noTransition, - page: () => const DownloadedView(), - binding: BindingsBuilder(() { - Get.lazyPut( - () => DownloadedController(), - ); - }), - ), - GetPage( - name: _Paths.setting, - transition: Transition.noTransition, - page: () => const SettingView(), - binding: BindingsBuilder(() { - Get.lazyPut(() => SettingController()); - }), - ) - ]), - ]), - ]; -}