mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-05 09:27:43 +08:00
feat: ui improvement
This commit is contained in:
@@ -161,15 +161,15 @@ class _SubmitSearchResultState extends ConsumerState<SubmitSearchResult> {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
child: const Text('确定'),
|
||||
LoadingTextButton(
|
||||
// style: TextButton.styleFrom(
|
||||
// textStyle: Theme.of(context).textTheme.labelLarge,
|
||||
// ),
|
||||
label: const Text('确定'),
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.saveAndValidate()) {
|
||||
final values = _formKey.currentState!.value;
|
||||
var f = ref
|
||||
await ref
|
||||
.read(searchPageDataProvider(widget.query).notifier)
|
||||
.submit2Watchlist(
|
||||
widget.item.id!,
|
||||
@@ -185,7 +185,6 @@ class _SubmitSearchResultState extends ConsumerState<SubmitSearchResult> {
|
||||
Navigator.of(context).pop();
|
||||
showSnakeBar("添加成功:${widget.item.name}");
|
||||
});
|
||||
showLoadingWithFuture(f);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -22,12 +22,11 @@ Future<void> showSettingDialog(
|
||||
),
|
||||
actions: <Widget>[
|
||||
showDelete
|
||||
? TextButton(
|
||||
onPressed: () {
|
||||
final f = onDelete().then((v) => Navigator.of(context).pop());
|
||||
showLoadingWithFuture(f);
|
||||
? LoadingTextButton(
|
||||
onPressed: () async {
|
||||
await onDelete().then((v) => Navigator.of(context).pop());
|
||||
},
|
||||
child: const Text(
|
||||
label: const Text(
|
||||
'删除',
|
||||
style: TextStyle(color: Colors.red),
|
||||
))
|
||||
@@ -35,11 +34,10 @@ Future<void> showSettingDialog(
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('取消')),
|
||||
TextButton(
|
||||
child: const Text('确定'),
|
||||
onPressed: () {
|
||||
final f = onSubmit().then((v) => Navigator.of(context).pop());
|
||||
showLoadingWithFuture(f);
|
||||
LoadingTextButton(
|
||||
label: const Text('确定'),
|
||||
onPressed: () async {
|
||||
await onSubmit().then((v) => Navigator.of(context).pop());
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -48,62 +48,55 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
Opacity(
|
||||
opacity: 0.7,
|
||||
child: ep.status == "downloading"
|
||||
? const Tooltip(
|
||||
message: "下载中",
|
||||
child: IconButton(onPressed: null, icon: Icon(Icons.downloading)),
|
||||
)
|
||||
? const IconButton(
|
||||
tooltip: "下载中",
|
||||
onPressed: null,
|
||||
icon: Icon(Icons.downloading))
|
||||
: (ep.status == "downloaded"
|
||||
? const Tooltip(
|
||||
message: "已下载",
|
||||
child: IconButton(onPressed: null, icon: Icon(Icons.download_done)),
|
||||
)
|
||||
? const IconButton(
|
||||
tooltip: "已下载",
|
||||
onPressed: null,
|
||||
icon: Icon(Icons.download_done))
|
||||
: (ep.monitored == true
|
||||
? Tooltip(
|
||||
message: "监控中",
|
||||
? IconButton(
|
||||
tooltip: "监控中",
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(mediaDetailsProvider(
|
||||
widget.seriesId)
|
||||
.notifier)
|
||||
.changeMonitoringStatus(
|
||||
ep.id!, false);
|
||||
},
|
||||
icon: const Icon(Icons.alarm))
|
||||
: Opacity(
|
||||
opacity: 0.7,
|
||||
child: IconButton(
|
||||
tooltip: "未监控",
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(mediaDetailsProvider(
|
||||
widget.seriesId)
|
||||
.notifier)
|
||||
.changeMonitoringStatus(
|
||||
ep.id!, false);
|
||||
ep.id!, true);
|
||||
},
|
||||
icon: const Icon(Icons.alarm)),
|
||||
)
|
||||
: Opacity(
|
||||
opacity: 0.7,
|
||||
child: Tooltip(
|
||||
message: "未监控",
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(mediaDetailsProvider(
|
||||
widget.seriesId)
|
||||
.notifier)
|
||||
.changeMonitoringStatus(
|
||||
ep.id!, true);
|
||||
},
|
||||
icon: const Icon(Icons.alarm_off)),
|
||||
),
|
||||
icon: const Icon(Icons.alarm_off)),
|
||||
)))),
|
||||
),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "搜索下载对应剧集",
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
var f = ref
|
||||
.read(mediaDetailsProvider(widget.seriesId)
|
||||
.notifier)
|
||||
.searchAndDownload(widget.seriesId,
|
||||
ep.seasonNumber!, ep.episodeNumber!)
|
||||
.then((v) => showSnakeBar("开始下载: $v"));
|
||||
showLoadingWithFuture(f);
|
||||
},
|
||||
icon: const Icon(Icons.download)),
|
||||
),
|
||||
LoadingIconButton(
|
||||
tooltip: "搜索下载对应剧集",
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(
|
||||
mediaDetailsProvider(widget.seriesId).notifier)
|
||||
.searchAndDownload(widget.seriesId,
|
||||
ep.seasonNumber!, ep.episodeNumber!)
|
||||
.then((v) => showSnakeBar("开始下载: $v"));
|
||||
},
|
||||
icon: Icons.download),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
@@ -142,19 +135,17 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
DataColumn(
|
||||
label: Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: "搜索下载全部剧集",
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
final f = ref
|
||||
.read(mediaDetailsProvider(widget.seriesId)
|
||||
.notifier)
|
||||
.searchAndDownload(widget.seriesId, k, 0)
|
||||
.then((v) => showSnakeBar("开始下载: $v"));
|
||||
showLoadingWithFuture(f);
|
||||
},
|
||||
icon: const Icon(Icons.download)),
|
||||
),
|
||||
LoadingIconButton(
|
||||
tooltip: "搜索下载全部剧集",
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(mediaDetailsProvider(widget.seriesId)
|
||||
.notifier)
|
||||
.searchAndDownload(widget.seriesId, k, 0)
|
||||
.then((v) => showSnakeBar("开始下载: $v"));
|
||||
//showLoadingWithFuture(f);
|
||||
},
|
||||
icon: Icons.download),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
|
||||
@@ -66,8 +66,7 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
const SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
Text(
|
||||
"${widget.details.storage!.name}:"),
|
||||
Text("${widget.details.storage!.name}:"),
|
||||
Text(
|
||||
"${widget.details.mediaType == "tv" ? widget.details.storage!.tvPath : widget.details.storage!.moviePath}"
|
||||
"${widget.details.targetDir}"),
|
||||
@@ -90,13 +89,15 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
const Text(""),
|
||||
Expanded(
|
||||
child: Text(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
overflow: TextOverflow.visible,
|
||||
maxLines: 9,
|
||||
widget.details.overview ?? "",
|
||||
)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
editIcon(widget.details),
|
||||
downloadButton(),
|
||||
editIcon(),
|
||||
deleteIcon(),
|
||||
],
|
||||
)
|
||||
@@ -113,11 +114,10 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
}
|
||||
|
||||
Widget deleteIcon() {
|
||||
return Tooltip(
|
||||
message: widget.details.mediaType == "tv" ? "删除剧集" : "删除电影",
|
||||
child: IconButton(
|
||||
onPressed: () => showConfirmDialog(), icon: const Icon(Icons.delete)),
|
||||
);
|
||||
return IconButton(
|
||||
tooltip: widget.details.mediaType == "tv" ? "删除剧集" : "删除电影",
|
||||
onPressed: () => showConfirmDialog(),
|
||||
icon: const Icon(Icons.delete));
|
||||
}
|
||||
|
||||
Future<void> showConfirmDialog() {
|
||||
@@ -150,19 +150,21 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget editIcon(SeriesDetails details) {
|
||||
Widget editIcon() {
|
||||
return IconButton(
|
||||
onPressed: () => showEditDialog(details), icon: const Icon(Icons.edit));
|
||||
tooltip: "编辑",
|
||||
onPressed: () => showEditDialog(),
|
||||
icon: const Icon(Icons.edit));
|
||||
}
|
||||
|
||||
showEditDialog(SeriesDetails details) {
|
||||
showEditDialog() {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("编辑 ${details.name}"),
|
||||
title: Text("编辑 ${widget.details.name}"),
|
||||
content: SelectionArea(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
@@ -171,11 +173,16 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
child: FormBuilder(
|
||||
key: _formKey,
|
||||
initialValue: {
|
||||
"resolution": details.resolution,
|
||||
"target_dir": details.targetDir,
|
||||
"limiter": details.limiter != null
|
||||
? RangeValues(details.limiter!.sizeMin.toDouble()/1000/1000,
|
||||
details.limiter!.sizeMax.toDouble()/1000/1000)
|
||||
"resolution": widget.details.resolution,
|
||||
"target_dir": widget.details.targetDir,
|
||||
"limiter": widget.details.limiter != null
|
||||
? RangeValues(
|
||||
widget.details.limiter!.sizeMin.toDouble() /
|
||||
1000 /
|
||||
1000,
|
||||
widget.details.limiter!.sizeMax.toDouble() /
|
||||
1000 /
|
||||
1000)
|
||||
: const RangeValues(0, 0)
|
||||
},
|
||||
child: Column(
|
||||
@@ -204,23 +211,29 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text("取消")),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
LoadingTextButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.saveAndValidate()) {
|
||||
final values = _formKey.currentState!.value;
|
||||
var f = ref
|
||||
await ref
|
||||
.read(mediaDetailsProvider(widget.details.id.toString())
|
||||
.notifier)
|
||||
.edit(values["resolution"], values["target_dir"],
|
||||
values["limiter"])
|
||||
.then((v) => Navigator.of(context).pop());
|
||||
showLoadingWithFuture(f);
|
||||
}
|
||||
},
|
||||
child: const Text("确认"))
|
||||
label: const Text("确认"))
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget downloadButton() {
|
||||
return IconButton(
|
||||
tooltip: widget.details.mediaType == "tv" ? "查找并下载所有监控剧集" : "查找并下载此电影",
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.download_rounded));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@ class ResourceList extends ConsumerWidget {
|
||||
: "-")));
|
||||
}
|
||||
|
||||
rows.add(DataCell(IconButton(
|
||||
icon: const Icon(Icons.download),
|
||||
rows.add(DataCell(LoadingIconButton(
|
||||
icon: Icons.download,
|
||||
onPressed: () async {
|
||||
var f = ref
|
||||
await ref
|
||||
.read(mediaTorrentsDataProvider((
|
||||
mediaId: mediaId,
|
||||
seasonNumber: seasonNum,
|
||||
@@ -70,7 +70,6 @@ class ResourceList extends ConsumerWidget {
|
||||
)).notifier)
|
||||
.download(torrent)
|
||||
.then((v) => showSnakeBar("开始下载:${torrent.name}"));
|
||||
showLoadingWithFuture(f);
|
||||
},
|
||||
)));
|
||||
return DataRow(cells: rows);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/widgets/utils.dart';
|
||||
|
||||
class Commons {
|
||||
static InputDecoration requiredTextFieldStyle({
|
||||
@@ -139,3 +140,93 @@ class _MySliderState extends State<MyRangeSlider> {
|
||||
return "$v MB";
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingIconButton extends StatefulWidget {
|
||||
LoadingIconButton({required this.onPressed, required this.icon, this.tooltip});
|
||||
final Future<void> Function() onPressed;
|
||||
final IconData icon;
|
||||
final String? tooltip;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _LoadingIconButtonState();
|
||||
}
|
||||
}
|
||||
|
||||
class _LoadingIconButtonState extends State<LoadingIconButton> {
|
||||
bool loading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
tooltip: widget.tooltip,
|
||||
onPressed: loading
|
||||
? null
|
||||
: () async {
|
||||
setState(() => loading = true);
|
||||
try {
|
||||
await widget.onPressed();
|
||||
} catch (e) {
|
||||
showSnakeBar("操作失败:$e");
|
||||
} finally {
|
||||
setState(() => loading = false);
|
||||
}
|
||||
},
|
||||
icon: loading
|
||||
? Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.grey,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
)
|
||||
: Icon(widget.icon));
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingTextButton extends StatefulWidget {
|
||||
LoadingTextButton({required this.onPressed, required this.label});
|
||||
final Future<void> Function() onPressed;
|
||||
final Widget label;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _LoadingTextButtonState();
|
||||
}
|
||||
}
|
||||
|
||||
class _LoadingTextButtonState extends State<LoadingTextButton> {
|
||||
bool loading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton.icon(
|
||||
onPressed: loading
|
||||
? null
|
||||
: () async {
|
||||
setState(() => loading = true);
|
||||
try {
|
||||
await widget.onPressed();
|
||||
} catch (e) {
|
||||
showSnakeBar("操作失败:$e");
|
||||
} finally {
|
||||
setState(() => loading = false);
|
||||
}
|
||||
},
|
||||
icon: loading
|
||||
? Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.grey,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
)
|
||||
: Text(""),
|
||||
label: widget.label,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user