feat: seperate active and archived activities

This commit is contained in:
Simon Ding
2024-07-23 22:22:53 +08:00
parent 55f5ce329c
commit 730db5c94a
3 changed files with 101 additions and 38 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"polaris/ent" "polaris/ent"
"polaris/ent/episode" "polaris/ent/episode"
"polaris/ent/history"
"polaris/log" "polaris/log"
"polaris/pkg/utils" "polaris/pkg/utils"
"strconv" "strconv"
@@ -18,9 +19,16 @@ type Activity struct {
} }
func (s *Server) GetAllActivities(c *gin.Context) (interface{}, error) { func (s *Server) GetAllActivities(c *gin.Context) (interface{}, error) {
q := c.Query("status")
his := s.db.GetHistories() his := s.db.GetHistories()
var activities = make([]Activity, 0, len(his)) var activities = make([]Activity, 0, len(his))
for _, h := range his { for _, h := range his {
if q == "active" && (h.Status != history.StatusRunning && h.Status != history.StatusUploading) {
continue //active downloads
} else if q == "archive" && (h.Status == history.StatusRunning || h.Status == history.StatusUploading) {
continue //archived downloads
}
a := Activity{ a := Activity{
History: h, History: h,
} }

View File

@@ -5,39 +5,88 @@ import 'package:ui/providers/activity.dart';
import 'package:ui/utils.dart'; import 'package:ui/utils.dart';
import 'package:ui/widgets/progress_indicator.dart'; import 'package:ui/widgets/progress_indicator.dart';
class ActivityPage extends ConsumerWidget { class ActivityPage extends ConsumerStatefulWidget {
const ActivityPage({super.key});
static const route = "/activities"; static const route = "/activities";
const ActivityPage({super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { _ActivityPageState createState() => _ActivityPageState();
var activitiesWatcher = ref.watch(activitiesDataProvider); }
return activitiesWatcher.when( class _ActivityPageState extends ConsumerState<ActivityPage>
data: (activities) { with TickerProviderStateMixin {
return SingleChildScrollView( late TabController _nestedTabController;
child: PaginatedDataTable( @override
rowsPerPage: 10, void initState() {
columns: const [ super.initState();
DataColumn(label: Text("#"), numeric: true), _nestedTabController = new TabController(length: 2, vsync: this);
DataColumn(label: Text("名称")),
DataColumn(label: Text("开始时间")),
DataColumn(label: Text("状态")),
DataColumn(label: Text("操作"))
],
source: ActivityDataSource(
activities: activities, onDelete: onDelete(ref)),
),
);
},
error: (err, trace) => Text("$err"),
loading: () => const MyProgressIndicator());
} }
Function(int) onDelete(WidgetRef ref) { @override
void dispose() {
super.dispose();
_nestedTabController.dispose();
}
int selectedTab = 0;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TabBar(
controller: _nestedTabController,
isScrollable: true,
onTap: (value) {
setState(() {
selectedTab = value;
});
},
tabs: const <Widget>[
Tab(
text: "下载中",
),
Tab(
text: "历史记录",
),
],
),
Builder(builder: (context) {
var activitiesWatcher = ref.watch(activitiesDataProvider("active"));
if (selectedTab == 1) {
activitiesWatcher = ref.watch(activitiesDataProvider("archive"));
}
return activitiesWatcher.when(
data: (activities) {
return SingleChildScrollView(
child: PaginatedDataTable(
rowsPerPage: 10,
columns: const [
DataColumn(label: Text("#"), numeric: true),
DataColumn(label: Text("名称")),
DataColumn(label: Text("开始时间")),
DataColumn(label: Text("状态")),
DataColumn(label: Text("操作"))
],
source: ActivityDataSource(
activities: activities,
onDelete: selectedTab == 0 ? onDelete() : null),
),
);
},
error: (err, trace) => Text("$err"),
loading: () => const MyProgressIndicator());
})
],
);
}
Function(int) onDelete() {
return (id) { return (id) {
ref ref
.read(activitiesDataProvider.notifier) .read(activitiesDataProvider("active").notifier)
.deleteActivity(id) .deleteActivity(id)
.whenComplete(() => Utils.showSnakeBar("删除成功")) .whenComplete(() => Utils.showSnakeBar("删除成功"))
.onError((error, trace) => Utils.showSnakeBar("删除失败:$error")); .onError((error, trace) => Utils.showSnakeBar("删除失败:$error"));
@@ -47,8 +96,8 @@ class ActivityPage extends ConsumerWidget {
class ActivityDataSource extends DataTableSource { class ActivityDataSource extends DataTableSource {
List<Activity> activities; List<Activity> activities;
Function(int) onDelete; Function(int)? onDelete;
ActivityDataSource({required this.activities, required this.onDelete}); ActivityDataSource({required this.activities, this.onDelete});
@override @override
int get rowCount => activities.length; int get rowCount => activities.length;
@@ -96,11 +145,13 @@ class ActivityDataSource extends DataTableSource {
progressColor: Colors.green, progressColor: Colors.green,
); );
}()), }()),
DataCell(Tooltip( onDelete != null
message: "删除任务", ? DataCell(Tooltip(
child: IconButton( message: "删除任务",
onPressed: () => onDelete(activity.id!), child: IconButton(
icon: const Icon(Icons.delete)))) onPressed: () => onDelete!(activity.id!),
icon: const Icon(Icons.delete))))
: const DataCell(Text(""))
]); ]);
} }

View File

@@ -5,7 +5,7 @@ import 'package:ui/providers/APIs.dart';
import 'package:ui/providers/server_response.dart'; import 'package:ui/providers/server_response.dart';
var activitiesDataProvider = var activitiesDataProvider =
AsyncNotifierProvider.autoDispose<ActivityData, List<Activity>>( AsyncNotifierProvider.autoDispose.family<ActivityData, List<Activity>, String>(
ActivityData.new); ActivityData.new);
var mediaHistoryDataProvider = FutureProvider.autoDispose.family( var mediaHistoryDataProvider = FutureProvider.autoDispose.family(
@@ -24,14 +24,18 @@ var mediaHistoryDataProvider = FutureProvider.autoDispose.family(
}, },
); );
class ActivityData extends AutoDisposeAsyncNotifier<List<Activity>> { class ActivityData
extends AutoDisposeFamilyAsyncNotifier<List<Activity>, String> {
@override @override
FutureOr<List<Activity>> build() async { FutureOr<List<Activity>> build(String arg) async {
Timer( if (arg == "active") {
const Duration(seconds: 5), ref.invalidateSelf); //Periodically Refresh //refresh active downloads
Timer(const Duration(seconds: 5),
ref.invalidateSelf); //Periodically Refresh
}
final dio = await APIs.getDio(); final dio = await APIs.getDio();
var resp = await dio.get(APIs.activityUrl); var resp = await dio.get(APIs.activityUrl, queryParameters: {"status": arg});
final sp = ServerResponse.fromJson(resp.data); final sp = ServerResponse.fromJson(resp.data);
if (sp.code != 0) { if (sp.code != 0) {
throw sp.message; throw sp.message;