feat: display loading animation

This commit is contained in:
Simon Ding
2024-07-30 14:02:24 +08:00
parent e2bba8ec71
commit 233970ef39
11 changed files with 121 additions and 124 deletions

View File

@@ -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)

View File

@@ -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<ActivityPage>
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<ActivityPage>
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);
};
}
}

View File

@@ -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<MovieDetailsPage> {
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<MovieDetailsPage> {
),
const Text(""),
Text(
details.overview!,
details.overview??"",
),
],
)),
@@ -109,16 +112,12 @@ class _MovieDetailsPageState extends ConsumerState<MovieDetailsPage> {
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<NestedTabBar>
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);
},
))
]);

View File

@@ -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<SearchPage> {
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<SearchPage> {
),
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<SearchPage> {
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);
},
),
],

View File

@@ -18,7 +18,7 @@ class AuthSettings extends ConsumerStatefulWidget {
}
class _AuthState extends ConsumerState<AuthSettings> {
final _formKey2 = GlobalKey<FormBuilderState>();
final _formKey2 = GlobalKey<FormBuilderState>();
bool? _enableAuth;
@override
@@ -84,12 +84,11 @@ class _AuthState extends ConsumerState<AuthSettings> {
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<AuthSettings> {
error: (err, trace) => Text("$err"),
loading: () => const MyProgressIndicator());
}
}
}

View File

@@ -1,8 +1,13 @@
import 'package:flutter/material.dart';
import 'package:ui/widgets/utils.dart';
import 'package:ui/widgets/widgets.dart';
Future<void> showSettingDialog(BuildContext context,String title, bool showDelete, Widget body,
Future Function() onSubmit, Future Function() onDelete) {
Future<void> showSettingDialog(
BuildContext context,
String title,
bool showDelete,
Widget body,
Future Function() onSubmit,
Future Function() onDelete) {
return showDialog<void>(
context: context,
barrierDismissible: true,
@@ -19,13 +24,8 @@ Future<void> 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<void> 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);
},
),
],

View File

@@ -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<GeneralSettings> {
final _formKey = GlobalKey<FormBuilderState>();
final _formKey = GlobalKey<FormBuilderState>();
@override
Widget build(BuildContext context) {
@@ -96,12 +95,8 @@ class _GeneralState extends ConsumerState<GeneralSettings> {
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<GeneralSettings> {
error: (err, trace) => Text("$err"),
loading: () => const MyProgressIndicator());
}
}
}

View File

@@ -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<TvDetailsPage> {
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<TvDetailsPage> {
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<TvDetailsPage> {
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<TvDetailsPage> {
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<TvDetailsPage> {
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<TvDetailsPage> {
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<TvDetailsPage> {
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);
},
))
]);

View File

@@ -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),

View File

@@ -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;

View File

@@ -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: <Widget>[
CircularProgressIndicator(),
Padding(
padding: EdgeInsets.only(top: 26.0),
child: Text("正在处理,请稍后..."),
)
],
),
);
}
});
},
);
}