From aaa006a32210e4beda277cf8da75dbf39463e99c Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Sun, 10 Nov 2024 13:28:21 +0800 Subject: [PATCH] WIP: init wizard --- ui/lib/init_wizard.dart | 190 ++++++++++++++++++++++++++++++++++ ui/lib/main.dart | 177 ++++++++++++++++--------------- ui/lib/settings/prowlarr.dart | 3 +- 3 files changed, 287 insertions(+), 83 deletions(-) create mode 100644 ui/lib/init_wizard.dart diff --git a/ui/lib/init_wizard.dart b/ui/lib/init_wizard.dart new file mode 100644 index 0000000..b8f19c8 --- /dev/null +++ b/ui/lib/init_wizard.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:ui/settings/prowlarr.dart'; +import 'package:ui/widgets/widgets.dart'; + +class InitWizard extends ConsumerStatefulWidget { + const InitWizard({super.key}); + static final String route = "/init_wizard"; + @override + ConsumerState createState() { + return _InitWizardState(); + } +} + +class _InitWizardState extends ConsumerState { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SelectionArea( + child: Container( + padding: EdgeInsets.all(50), + child: ListView( + children: [ + Container( + alignment: Alignment.center, + child: Text( + "Polaris 影视追踪下载", + style: TextStyle( + fontSize: 30, + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold), + ), + ), + Container( + padding: EdgeInsets.only(left: 10, top: 30, bottom: 30), + child: Text( + "设置向导", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary), + ), + ), + tmdbSetting(), + downloaderSetting(), + indexerSetting(), + ], + ), + )), + ); + } + + Widget tmdbSetting() { + return ExpansionTile( + title: Text( + "第一步:TMDB设置", + style: TextStyle(fontWeight: FontWeight.bold), + ), + childrenPadding: EdgeInsets.only(left: 100, right: 20), + initiallyExpanded: true, + children: [ + Container( + alignment: Alignment.topLeft, + child: Text("TMDB API Key 设置,用来获取各种影视的信息,API Key获取方式参考官网"), + ), + FormBuilder( + child: Column( + children: [ + FormBuilderTextField( + name: "tmdb", + decoration: InputDecoration(labelText: "TMDB API Key"), + ), + Center( + child: Padding( + padding: EdgeInsets.all(10), + child: ElevatedButton(onPressed: null, child: Text("保存")), + )) + ], + )) + ], + ); + } + + Widget indexerSetting() { + return ExpansionTile( + initiallyExpanded: true, + childrenPadding: EdgeInsets.only(left: 100, right: 20), + title: Text( + "第三步:Prowlarr设置", + style: TextStyle(fontWeight: FontWeight.bold), + ), + children: [ProwlarrSettingPage()], + ); + } + + Widget downloaderSetting() { + final _formKey = GlobalKey(); + var _enableAuth = false; + String selectImpl = "transmission"; + + return ExpansionTile( + childrenPadding: EdgeInsets.only(left: 100, right: 20), + initiallyExpanded: true, + title: Text("第二步:下载客户端", style: TextStyle(fontWeight: FontWeight.bold)), + children: [ + StatefulBuilder(builder: (BuildContext context, StateSetter setState) { + return FormBuilder( + key: _formKey, + initialValue: { + "name": "client.name", + "url": "client.url", + "user": "client.user", + "password": "client.password", + "impl": selectImpl, + }, + child: Column( + children: [ + FormBuilderDropdown( + name: "impl", + decoration: const InputDecoration(labelText: "类型"), + items: const [ + DropdownMenuItem( + value: "transmission", child: Text("Transmission")), + DropdownMenuItem( + value: "qbittorrent", child: Text("qBittorrent")), + ], + validator: FormBuilderValidators.required(), + ), + FormBuilderTextField( + name: "name", + decoration: const InputDecoration(labelText: "名称"), + validator: FormBuilderValidators.required(), + autovalidateMode: AutovalidateMode.onUserInteraction), + FormBuilderTextField( + name: "url", + decoration: const InputDecoration( + labelText: "地址", hintText: "http://127.0.0.1:9091"), + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: FormBuilderValidators.required(), + ), + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Column( + children: [ + FormBuilderSwitch( + name: "auth", + title: const Text("需要认证"), + initialValue: _enableAuth, + onChanged: (v) { + setState(() { + _enableAuth = v!; + }); + }), + _enableAuth + ? Column( + children: [ + FormBuilderTextField( + name: "user", + decoration: + Commons.requiredTextFieldStyle( + text: "用户"), + validator: + FormBuilderValidators.required(), + autovalidateMode: + AutovalidateMode.onUserInteraction), + FormBuilderTextField( + name: "password", + decoration: + Commons.requiredTextFieldStyle( + text: "密码"), + validator: + FormBuilderValidators.required(), + obscureText: true, + autovalidateMode: + AutovalidateMode.onUserInteraction), + ], + ) + : Container() + ], + ); + }) + ], + )); + }), + ], + ); + } +} diff --git a/ui/lib/main.dart b/ui/lib/main.dart index 877178e..d88ce70 100644 --- a/ui/lib/main.dart +++ b/ui/lib/main.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:ui/activity.dart'; import 'package:ui/calendar.dart'; +import 'package:ui/init_wizard.dart'; import 'package:ui/login_page.dart'; import 'package:ui/movie_watchlist.dart'; import 'package:ui/providers/APIs.dart'; @@ -124,9 +125,17 @@ class _MyAppState extends ConsumerState { initialLocation: WelcomePage.routeTv, routes: [ shellRoute, + GoRoute( + path: "/", + redirect: (context, state) => WelcomePage.routeTv, + ), GoRoute( path: LoginScreen.route, builder: (context, state) => const LoginScreen(), + ), + GoRoute( + path: InitWizard.route, + builder: (context, state) => const InitWizard(), ) ], ); @@ -171,88 +180,7 @@ class _MainSkeletonState extends State { var padding = isSmallScreen(context) ? 5.0 : 20.0; return AdaptiveScaffold( appBarBreakpoint: Breakpoints.standard, - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - leading: Container( - alignment: Alignment.centerLeft, - child: TextButton( - onPressed: () => context.go(WelcomePage.routeTv), - child: const Text( - "Polaris", - overflow: TextOverflow.clip, - style: TextStyle(fontSize: 28), - ), - ), - ), - leadingWidth: isSmallScreen(context) ? 0 : 190, - title: Container( - alignment: Alignment.bottomLeft, - child: SearchAnchor( - builder: (BuildContext context, SearchController controller) { - return Container( - constraints: const BoxConstraints(maxWidth: 250, maxHeight: 40), - child: Opacity( - opacity: 0.8, - child: SearchBar( - hintText: "在此搜索...", - leading: const Icon(Icons.search), - controller: controller, - shadowColor: WidgetStateColor.transparent, - backgroundColor: WidgetStatePropertyAll( - Theme.of(context).colorScheme.primaryContainer), - onSubmitted: (value) => context.go(Uri( - path: SearchPage.route, - queryParameters: {'query': value}).toString()), - ), - ), - ); - }, suggestionsBuilder: - (BuildContext context, SearchController controller) { - return [Text("dadada")]; - }), - ), - - actions: [ - // IconButton( - // onPressed: () => showCalendar(context), - // icon: Icon(Icons.calendar_month)), - IconButton( - onPressed: () => showDonate(context), - icon: Icon( - Icons.favorite_rounded, - color: Colors.red, - )), - - MenuAnchor( - menuChildren: [ - MenuItemButton( - leadingIcon: const Icon(Icons.exit_to_app), - child: const Text("登出"), - onPressed: () async { - await APIs.logout(); - }, - ), - ], - builder: (context, controller, child) { - return TextButton( - onPressed: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - child: const Icon(Icons.account_circle), - ); - }, - ), - ], - ), + appBar: appBar(), useDrawer: false, selectedIndex: widget.body.currentIndex, onSelectedIndexChange: (p0) => widget.body @@ -314,4 +242,89 @@ class _MainSkeletonState extends State { }, ); } + + AppBar appBar() { + return AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + leading: Container( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () => context.go(WelcomePage.routeTv), + child: const Text( + "Polaris", + overflow: TextOverflow.clip, + style: TextStyle(fontSize: 28), + ), + ), + ), + leadingWidth: isSmallScreen(context) ? 0 : 190, + title: Container( + alignment: Alignment.bottomLeft, + child: SearchAnchor( + builder: (BuildContext context, SearchController controller) { + return Container( + constraints: const BoxConstraints(maxWidth: 250, maxHeight: 40), + child: Opacity( + opacity: 0.8, + child: SearchBar( + hintText: "在此搜索...", + leading: const Icon(Icons.search), + controller: controller, + shadowColor: WidgetStateColor.transparent, + backgroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.primaryContainer), + onSubmitted: (value) => context.go(Uri( + path: SearchPage.route, + queryParameters: {'query': value}).toString()), + ), + ), + ); + }, suggestionsBuilder: + (BuildContext context, SearchController controller) { + return [Text("dadada")]; + }), + ), + + actions: [ + // IconButton( + // onPressed: () => showCalendar(context), + // icon: Icon(Icons.calendar_month)), + IconButton( + onPressed: () => showDonate(context), + icon: Icon( + Icons.favorite_rounded, + color: Colors.red, + )), + + MenuAnchor( + menuChildren: [ + MenuItemButton( + leadingIcon: const Icon(Icons.exit_to_app), + child: const Text("登出"), + onPressed: () async { + await APIs.logout(); + }, + ), + ], + builder: (context, controller, child) { + return TextButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: const Icon(Icons.account_circle), + ); + }, + ), + ], + ); + } } diff --git a/ui/lib/settings/prowlarr.dart b/ui/lib/settings/prowlarr.dart index 82a2ba8..958c97f 100644 --- a/ui/lib/settings/prowlarr.dart +++ b/ui/lib/settings/prowlarr.dart @@ -51,7 +51,8 @@ class ProwlarrSettingState extends ConsumerState { FormBuilderSwitch( name: "disabled", title: const Text("禁用 Prowlarr"), - decoration: InputDecoration(icon: Icon(Icons.do_not_disturb)), + decoration: + InputDecoration(icon: Icon(Icons.do_not_disturb)), ), Center( child: Padding(