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