From 233970ef3950798cb6006f5352826ba9bbab0ee2 Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Tue, 30 Jul 2024 14:02:24 +0800 Subject: [PATCH] feat: display loading animation --- server/storage.go | 6 ++--- ui/lib/activity.dart | 11 ++++---- ui/lib/movie_watchlist.dart | 30 ++++++++++----------- ui/lib/search.dart | 24 +++++------------ ui/lib/settings/auth.dart | 14 +++++----- ui/lib/settings/dialog.dart | 31 +++++++++------------- ui/lib/settings/general.dart | 16 ++++------- ui/lib/tv_details.dart | 51 ++++++++++++++---------------------- ui/lib/welcome_page.dart | 2 +- ui/lib/widgets/utils.dart | 15 +++-------- ui/lib/widgets/widgets.dart | 45 +++++++++++++++++++++++++++++++ 11 files changed, 121 insertions(+), 124 deletions(-) diff --git a/server/storage.go b/server/storage.go index eb2e758..de30a0c 100644 --- a/server/storage.go +++ b/server/storage.go @@ -69,7 +69,7 @@ func (s *Server) SuggestedSeriesFolderName(c *gin.Context) (interface{}, error) year := strings.Split(d.FirstAirDate, "-")[0] - if utils.ContainsChineseChar(originalName) { + if utils.ContainsChineseChar(originalName) || name == originalName { name = originalName } else { name = fmt.Sprintf("%s %s", name, originalName) @@ -95,9 +95,7 @@ func (s *Server) SuggestedMovieFolderName(c *gin.Context) (interface{}, error) { originalName := d1.OriginalTitle year := strings.Split(d1.ReleaseDate, "-")[0] - name = fmt.Sprintf("%s %s", name, originalName) - - if utils.ContainsChineseChar(originalName) { + if utils.ContainsChineseChar(originalName) || name == originalName { name = originalName } else { name = fmt.Sprintf("%s %s", name, originalName) diff --git a/ui/lib/activity.dart b/ui/lib/activity.dart index 8e7b47d..6bdc3f2 100644 --- a/ui/lib/activity.dart +++ b/ui/lib/activity.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:percent_indicator/circular_percent_indicator.dart'; import 'package:ui/providers/activity.dart'; -import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/progress_indicator.dart'; +import 'package:ui/widgets/widgets.dart'; class ActivityPage extends ConsumerStatefulWidget { const ActivityPage({super.key}); @@ -68,7 +68,7 @@ class _ActivityPageState extends ConsumerState DataColumn(label: Text("名称")), DataColumn(label: Text("开始时间")), DataColumn(label: Text("状态")), - DataColumn(label: Text("操作")) + DataColumn(label: Text("操作")) ], source: ActivityDataSource( activities: activities, @@ -85,11 +85,10 @@ class _ActivityPageState extends ConsumerState Function(int) onDelete() { return (id) { - ref + final f = ref .read(activitiesDataProvider("active").notifier) - .deleteActivity(id) - .then((v) => Utils.showSnakeBar("删除成功")) - .onError((error, trace) => Utils.showSnakeBar("删除失败:$error")); + .deleteActivity(id); + showLoadingWithFuture(f); }; } } diff --git a/ui/lib/movie_watchlist.dart b/ui/lib/movie_watchlist.dart index a901151..b7691b9 100644 --- a/ui/lib/movie_watchlist.dart +++ b/ui/lib/movie_watchlist.dart @@ -5,9 +5,10 @@ import 'package:ui/providers/APIs.dart'; import 'package:ui/providers/activity.dart'; import 'package:ui/providers/series_details.dart'; import 'package:ui/providers/settings.dart'; -import 'package:ui/widgets/utils.dart'; import 'package:ui/welcome_page.dart'; +import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/progress_indicator.dart'; +import 'package:ui/widgets/widgets.dart'; class MovieDetailsPage extends ConsumerStatefulWidget { static const route = "/movie/:id"; @@ -44,7 +45,9 @@ class _MovieDetailsPageState extends ConsumerState { image: DecorationImage( fit: BoxFit.cover, opacity: 0.3, - colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.3), BlendMode.dstATop), + colorFilter: ColorFilter.mode( + Colors.black.withOpacity(0.3), + BlendMode.dstATop), image: NetworkImage( "${APIs.imagesUrl}/${details.id}/backdrop.jpg", ))), @@ -101,7 +104,7 @@ class _MovieDetailsPageState extends ConsumerState { ), const Text(""), Text( - details.overview!, + details.overview??"", ), ], )), @@ -109,16 +112,12 @@ class _MovieDetailsPageState extends ConsumerState { children: [ IconButton( onPressed: () { - ref + var f = ref .read(mediaDetailsProvider( widget.id) .notifier) - .delete() - .then((v) => context - .go(WelcomePage.routeMoivie)) - .onError((error, trace) => - Utils.showSnakeBar( - "删除失败:$error")); + .delete().then((v) => context.go(WelcomePage.routeMoivie)); + showLoadingWithFuture(f); }, icon: const Icon(Icons.delete)) ], @@ -252,17 +251,18 @@ class _NestedTabBarState extends ConsumerState DataCell(IconButton( icon: const Icon(Icons.download), onPressed: () { - ref + final f = ref .read(mediaTorrentsDataProvider(( mediaId: widget.id, seasonNumber: 0, episodeNumber: 0 )).notifier) .download(torrent) - .then((v) => Utils.showSnakeBar( - "开始下载:${torrent.name}")) - .onError((error, trace) => - Utils.showSnakeBar("操作失败: $error")); + .then((v) => showSnakeBar( + "开始下载:${torrent.name}")); + // .onError((error, trace) => + // Utils.showSnakeBar("操作失败: $error")); + showLoadingWithFuture(f); }, )) ]); diff --git a/ui/lib/search.dart b/ui/lib/search.dart index 4460b32..ce1ace8 100644 --- a/ui/lib/search.dart +++ b/ui/lib/search.dart @@ -4,8 +4,9 @@ import 'package:go_router/go_router.dart'; import 'package:ui/providers/APIs.dart'; import 'package:ui/providers/settings.dart'; import 'package:ui/providers/welcome_data.dart'; -import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/progress_indicator.dart'; +import 'package:ui/widgets/utils.dart'; +import 'package:ui/widgets/widgets.dart'; class SearchPage extends ConsumerStatefulWidget { const SearchPage({super.key, this.query}); @@ -155,9 +156,9 @@ class _SearchPageState extends ConsumerState { String resSelected = "1080p"; int storageSelected = 0; var storage = ref.watch(storageSettingProvider); - var name = ref.watch(suggestNameDataProvider((id: item.id!, mediaType: item.mediaType!))); + var name = ref.watch(suggestNameDataProvider( + (id: item.id!, mediaType: item.mediaType!))); bool downloadHistoryEpisodes = false; - bool buttonTapped = false; var pathController = TextEditingController(); return AlertDialog( @@ -275,14 +276,7 @@ class _SearchPageState extends ConsumerState { ), child: const Text('确定'), onPressed: () async { - if (buttonTapped) { - return; - } - setState(() { - buttonTapped = true; - }); - - await ref + var f = ref .read(searchPageDataProvider(widget.query ?? "") .notifier) .submit2Watchlist( @@ -293,14 +287,10 @@ class _SearchPageState extends ConsumerState { pathController.text, downloadHistoryEpisodes) .then((v) { - Utils.showSnakeBar("添加成功"); Navigator.of(context).pop(); - }).onError((error, trace) { - Utils.showSnakeBar("添加失败:$error"); - }); - setState(() { - buttonTapped = false; + showSnakeBar("添加成功:${item.name}"); }); + showLoadingWithFuture(f); }, ), ], diff --git a/ui/lib/settings/auth.dart b/ui/lib/settings/auth.dart index 29b523e..d1b7981 100644 --- a/ui/lib/settings/auth.dart +++ b/ui/lib/settings/auth.dart @@ -18,7 +18,7 @@ class AuthSettings extends ConsumerStatefulWidget { } class _AuthState extends ConsumerState { - final _formKey2 = GlobalKey(); + final _formKey2 = GlobalKey(); bool? _enableAuth; @override @@ -84,12 +84,11 @@ class _AuthState extends ConsumerState { var f = ref .read(authSettingProvider.notifier) .updateAuthSetting(_enableAuth!, - values["user"], values["password"]); - f.then((v) { - Utils.showSnakeBar("更新成功"); - }).onError((e, s) { - Utils.showSnakeBar("更新失败:$e"); + values["user"], values["password"]) + .then((v) { + showSnakeBar("更新成功"); }); + showLoadingWithFuture(f); } })) ], @@ -98,5 +97,4 @@ class _AuthState extends ConsumerState { error: (err, trace) => Text("$err"), loading: () => const MyProgressIndicator()); } - -} \ No newline at end of file +} diff --git a/ui/lib/settings/dialog.dart b/ui/lib/settings/dialog.dart index dfff68b..947aae7 100644 --- a/ui/lib/settings/dialog.dart +++ b/ui/lib/settings/dialog.dart @@ -1,8 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:ui/widgets/utils.dart'; +import 'package:ui/widgets/widgets.dart'; -Future showSettingDialog(BuildContext context,String title, bool showDelete, Widget body, - Future Function() onSubmit, Future Function() onDelete) { +Future showSettingDialog( + BuildContext context, + String title, + bool showDelete, + Widget body, + Future Function() onSubmit, + Future Function() onDelete) { return showDialog( context: context, barrierDismissible: true, @@ -19,13 +24,8 @@ Future showSettingDialog(BuildContext context,String title, bool showDelet showDelete ? TextButton( onPressed: () { - final f = onDelete(); - f.then((v) { - Utils.showSnakeBar("删除成功"); - Navigator.of(context).pop(); - }).onError((e, s) { - Utils.showSnakeBar("删除失败:$e"); - }); + final f = onDelete().then((v) => Navigator.of(context).pop()); + showLoadingWithFuture(f); }, child: const Text( '删除', @@ -38,15 +38,8 @@ Future showSettingDialog(BuildContext context,String title, bool showDelet TextButton( child: const Text('确定'), onPressed: () { - final f = onSubmit(); - f.then((v) { - Utils.showSnakeBar("操作成功"); - Navigator.of(context).pop(); - }).onError((e, s) { - if (e.toString() != "validation_error") { - Utils.showSnakeBar("操作失败:$e"); - } - }); + final f = onSubmit().then((v) => Navigator.of(context).pop()); + showLoadingWithFuture(f); }, ), ], diff --git a/ui/lib/settings/general.dart b/ui/lib/settings/general.dart index bb48601..dd5bd30 100644 --- a/ui/lib/settings/general.dart +++ b/ui/lib/settings/general.dart @@ -3,8 +3,8 @@ 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/providers/settings.dart'; -import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/progress_indicator.dart'; +import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/widgets.dart'; class GeneralSettings extends ConsumerStatefulWidget { @@ -17,9 +17,8 @@ class GeneralSettings extends ConsumerStatefulWidget { } } - class _GeneralState extends ConsumerState { - final _formKey = GlobalKey(); + final _formKey = GlobalKey(); @override Widget build(BuildContext context) { @@ -96,12 +95,8 @@ class _GeneralState extends ConsumerState { tmdbApiKey: values["tmdb_api"], downloadDIr: values["download_dir"], logLevel: values["log_level"], - proxy: values["proxy"])); - f.then((v) { - Utils.showSnakeBar("更新成功"); - }).onError((e, s) { - Utils.showSnakeBar("更新失败:$e"); - }); + proxy: values["proxy"])).then((v) => showSnakeBar("更新成功")); + showLoadingWithFuture(f); } }), ), @@ -113,5 +108,4 @@ class _GeneralState extends ConsumerState { error: (err, trace) => Text("$err"), loading: () => const MyProgressIndicator()); } - -} \ No newline at end of file +} diff --git a/ui/lib/tv_details.dart b/ui/lib/tv_details.dart index e1cea61..8b37f2e 100644 --- a/ui/lib/tv_details.dart +++ b/ui/lib/tv_details.dart @@ -4,9 +4,10 @@ import 'package:go_router/go_router.dart'; import 'package:ui/providers/APIs.dart'; import 'package:ui/providers/series_details.dart'; import 'package:ui/providers/settings.dart'; -import 'package:ui/widgets/utils.dart'; import 'package:ui/welcome_page.dart'; +import 'package:ui/widgets/utils.dart'; import 'package:ui/widgets/progress_indicator.dart'; +import 'package:ui/widgets/widgets.dart'; class TvDetailsPage extends ConsumerStatefulWidget { static const route = "/series/:id"; @@ -70,14 +71,12 @@ class _TvDetailsPageState extends ConsumerState { message: "搜索下载对应剧集", child: IconButton( onPressed: () { - ref + var f = ref .read(mediaDetailsProvider(widget.seriesId) .notifier) .searchAndDownload(widget.seriesId, - ep.seasonNumber!, ep.episodeNumber!) - .then((v) => Utils.showSnakeBar("开始下载: $v")) - .onError((error, trace) => - Utils.showSnakeBar("操作失败: $error")); + ep.seasonNumber!, ep.episodeNumber!); + showLoadingWithFuture(f); }, icon: const Icon(Icons.download)), ), @@ -120,13 +119,11 @@ class _TvDetailsPageState extends ConsumerState { message: "搜索下载全部剧集", child: IconButton( onPressed: () { - ref + final f = ref .read(mediaDetailsProvider(widget.seriesId) .notifier) - .searchAndDownload(widget.seriesId, k, 0) - .then((v) => Utils.showSnakeBar("开始下载: $v")) - .onError((error, trace) => - Utils.showSnakeBar("操作失败: $error")); + .searchAndDownload(widget.seriesId, k, 0).then((v) => showSnakeBar("开始下载: $v")); + showLoadingWithFuture(f); }, icon: const Icon(Icons.download)), ), @@ -154,7 +151,8 @@ class _TvDetailsPageState extends ConsumerState { image: DecorationImage( fit: BoxFit.cover, opacity: 0.3, - colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.3), BlendMode.dstATop), + colorFilter: ColorFilter.mode( + Colors.black.withOpacity(0.3), BlendMode.dstATop), image: NetworkImage( "${APIs.imagesUrl}/${details.id}/backdrop.jpg"))), child: Padding( @@ -166,9 +164,8 @@ class _TvDetailsPageState extends ConsumerState { child: Padding( padding: const EdgeInsets.all(10), child: Image.network( - "${APIs.imagesUrl}/${details.id}/poster.jpg", - fit: BoxFit.contain - ), + "${APIs.imagesUrl}/${details.id}/poster.jpg", + fit: BoxFit.contain), ), ), Flexible( @@ -217,16 +214,12 @@ class _TvDetailsPageState extends ConsumerState { children: [ IconButton( onPressed: () { - ref + var f = ref .read(mediaDetailsProvider( widget.seriesId) .notifier) - .delete() - .then((v) => - context.go(WelcomePage.routeTv)) - .onError((error, trace) => - Utils.showSnakeBar( - "删除失败: $error")); + .delete().then((v) => context.go(WelcomePage.routeTv)); + showLoadingWithFuture(f); }, icon: const Icon(Icons.delete)) ], @@ -270,7 +263,8 @@ class _TvDetailsPageState extends ConsumerState { data: (v) { return SingleChildScrollView( child: DataTable( - dataTextStyle: const TextStyle(fontSize: 12, height: 0), + dataTextStyle: + const TextStyle(fontSize: 12, height: 0), columns: const [ DataColumn(label: Text("名称")), DataColumn(label: Text("大小")), @@ -289,19 +283,14 @@ class _TvDetailsPageState extends ConsumerState { DataCell(IconButton( icon: const Icon(Icons.download), onPressed: () async { - await ref + var f = ref .read(mediaTorrentsDataProvider(( mediaId: id, seasonNumber: season, episodeNumber: episode )).notifier) - .download(torrent) - .then((v) { - Navigator.of(context).pop(); - Utils.showSnakeBar( - "开始下载:${torrent.name}"); - }).onError((error, trace) => - Utils.showSnakeBar("下载失败:$error")); + .download(torrent).then((v) => showSnakeBar("开始下载:${torrent.name}")); + showLoadingWithFuture(f); }, )) ]); diff --git a/ui/lib/welcome_page.dart b/ui/lib/welcome_page.dart index 2e8c3ee..9cd9f36 100644 --- a/ui/lib/welcome_page.dart +++ b/ui/lib/welcome_page.dart @@ -74,7 +74,7 @@ class WelcomePage extends ConsumerWidget { ), Text( item.name!, - style: const TextStyle( + style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, height: 2.5), diff --git a/ui/lib/widgets/utils.dart b/ui/lib/widgets/utils.dart index 842f4f9..24748f4 100644 --- a/ui/lib/widgets/utils.dart +++ b/ui/lib/widgets/utils.dart @@ -32,7 +32,9 @@ class Utils { ); } - static showSnakeBar(String msg) { +} + + showSnakeBar(String msg) { final context = APIs.navigatorKey.currentContext; if (context != null) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( @@ -42,17 +44,6 @@ class Utils { } } - static bool showError(BuildContext context, AsyncSnapshot snapshot) { - final isErrored = snapshot.hasError && - snapshot.connectionState != ConnectionState.waiting; - if (isErrored) { - Utils.showSnakeBar("当前操作出错: ${snapshot.error}"); - return true; - } - return false; - } -} - extension FileFormatter on num { String readableFileSize({bool base1024 = true}) { final base = base1024 ? 1024 : 1000; diff --git a/ui/lib/widgets/widgets.dart b/ui/lib/widgets/widgets.dart index d3b2f6c..4fdaf43 100644 --- a/ui/lib/widgets/widgets.dart +++ b/ui/lib/widgets/widgets.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:ui/providers/APIs.dart'; class Commons { static InputDecoration requiredTextFieldStyle({ @@ -41,3 +42,47 @@ class SettingsCard extends StatelessWidget { ); } } + +showLoadingWithFuture(Future f) { + final context = APIs.navigatorKey.currentContext; + if (context == null) { + return; + } + showDialog( + context: context, + barrierDismissible: false, //点击遮罩不关闭对话框 + builder: (context) { + return FutureBuilder( + future: f, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return AlertDialog( + content: Text("处理失败:${snapshot.error}"), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text("好")) + ], + ); + } + Navigator.of(context).pop(); + return Container(); + } else { + return const AlertDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(), + Padding( + padding: EdgeInsets.only(top: 26.0), + child: Text("正在处理,请稍后..."), + ) + ], + ), + ); + } + }); + }, + ); +}