From 8f18c33a6340f501c1745da9f3b08c960efbebb7 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 20 Feb 2024 11:05:16 +0800 Subject: [PATCH] Bug fix --- lib/dialog/new_download_task_page.dart | 4 +- lib/task.dart | 60 ++++++++++++++++++++--- lib/task_manager.dart | 67 ++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 9 deletions(-) diff --git a/lib/dialog/new_download_task_page.dart b/lib/dialog/new_download_task_page.dart index 0f205de..64640be 100644 --- a/lib/dialog/new_download_task_page.dart +++ b/lib/dialog/new_download_task_page.dart @@ -88,7 +88,9 @@ class _NewDownloadTaskPage extends State { Widget build(BuildContext context) { tryInitApi(context); if (_ok) { - context.canPop() ? context.pop() : context.go("/task_manager"); + WidgetsBinding.instance!.addPostFrameCallback((_) { + context.canPop() ? context.pop() : context.go("/task_manager"); + }); } final i18n = AppLocalizations.of(context)!; final maxWidth = MediaQuery.of(context).size.width; diff --git a/lib/task.dart b/lib/task.dart index 8ba90d3..11a31a4 100644 --- a/lib/task.dart +++ b/lib/task.dart @@ -14,6 +14,7 @@ class TaskManager { bool _closed = false; bool _allowReconnect = true; Timer? _reconnectTimer; + List tasksList = []; void clear() { tasks.clear(); _channel?.stream.drain(); @@ -21,6 +22,29 @@ class TaskManager { _closed = true; } + void addToTasksList(Task task, TaskStatus status) { + if (status == TaskStatus.finished) { + tasksList.add(task.id); + return; + } + final index = tasksList.indexWhere((element) { + final otask = tasks[element]; + if (otask == null) { + return false; + } + if (status == TaskStatus.wait) { + return otask.status == TaskStatus.finished; + } else { + return otask.status == TaskStatus.wait; + } + }); + if (index == -1) { + tasksList.add(task.id); + } else { + tasksList.insert(index, task.id); + } + } + Future connect() async { if (auth.canManageTasks != true) return; try { @@ -32,36 +56,49 @@ class TaskManager { if (type == "tasks") { final list = TaskList.fromJson(data); for (var task in list.tasks) { + final status = list.running.contains(task.id) + ? TaskStatus.running + : TaskStatus.wait; tasks[task.id] = TaskDetail( base: task, - status: list.running.contains(task.id) - ? TaskStatus.running - : TaskStatus.wait, + status: status, ); + addToTasksList(task, status); } + listener.tryEmit("task_list_changed", null); } else if (type == "new_task") { final task = Task.fromJson(data["detail"] as Map); tasks[task.id] = TaskDetail( base: task, status: TaskStatus.wait, ); + addToTasksList(task, TaskStatus.wait); + listener.tryEmit("task_list_changed", null); } else if (type == "task_started") { final task = Task.fromJson(data["detail"] as Map); tasks.update(task.id, (value) { value.status = TaskStatus.running; + tasksList.remove(task.id); + tasksList.add(task.id); return value; - }, - ifAbsent: () => TaskDetail( - base: task, - status: TaskStatus.running, - )); + }, ifAbsent: () { + addToTasksList(task, TaskStatus.running); + return TaskDetail( + base: task, + status: TaskStatus.running, + ); + }); + listener.tryEmit("task_list_changed", null); } else if (type == "task_finished") { final task = Task.fromJson(data["detail"] as Map); if (tasks.containsKey(task.id)) { tasks.update(task.id, (value) { value.status = TaskStatus.finished; + tasksList.remove(task.id); + tasksList.add(task.id); return value; }); + listener.tryEmit("task_list_changed", null); } } else if (type == "task_progress") { final task = @@ -88,9 +125,16 @@ class TaskManager { value.status = TaskStatus.failed; value.error = info.error; value.fataled = info.fatal; + if (info.fatal) { + tasksList.remove(info.task.id); + tasksList.add(info.task.id); + listener.tryEmit("task_list_changed", null); + } return value; }); } + } else if (type == "ping") { + _channel?.sink.add("{\"type\":\"pong\"}"); } } catch (e) { _log.warning("Error processing task message: $e"); diff --git a/lib/task_manager.dart b/lib/task_manager.dart index 1afb370..95c34b5 100644 --- a/lib/task_manager.dart +++ b/lib/task_manager.dart @@ -1,5 +1,7 @@ +import 'dart:ui'; import 'package:enum_flag/enum_flag.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'api/task.dart'; @@ -37,6 +39,20 @@ class TaskStatusFilter { code |= flag.value; } + bool filter(TaskStatus status) { + if (isAll) return true; + switch (status) { + case TaskStatus.wait: + return has(TaskStatusFilterFlag.wait); + case TaskStatus.running: + return has(TaskStatusFilterFlag.running); + case TaskStatus.finished: + return has(TaskStatusFilterFlag.finished); + case TaskStatus.failed: + return has(TaskStatusFilterFlag.failed); + } + } + void remove(TaskStatusFilterFlag flag) { code &= ~flag.value; } @@ -57,9 +73,59 @@ class _TaskManagerPage extends State @override void initState() { _filter = TaskStatusFilter(); + listener.on("task_list_changed", _onStateChanged); super.initState(); } + @override + void dispose() { + listener.removeEventListener("task_list_changed", _onStateChanged); + super.dispose(); + } + + void _onStateChanged(dynamic _) { + setState(() {}); + } + + Widget _buildItem(BuildContext context, int index) { + final task = tasks.tasks[tasks.tasksList[index]]; + if (task == null) { + return Container(key: ValueKey("unknown_$index")); + } + if (!_filter.filter(task.status)) { + return Container(key: ValueKey("filtered_task_${task.base.id}")); + } + return Padding( + padding: const EdgeInsets.all(8), + key: ValueKey("task_${task.base.id}"), + child: Text("TODO ${task.base.id}")); + } + + Widget _proxyDecorator(Widget child, int index, Animation animation) { + return AnimatedBuilder( + animation: animation, + builder: (BuildContext context, Widget? child) { + final double animValue = Curves.easeInOut.transform(animation.value); + final double elevation = lerpDouble(0, 6, animValue)!; + return Material( + elevation: elevation, + child: child, + ); + }, + child: child, + ); + } + + void _onReorder(int oldIndex, int newIndex) {} + + Widget _buildList(BuildContext context) { + return SliverReorderableList( + itemBuilder: _buildItem, + itemCount: tasks.tasksList.length, + onReorder: _onReorder, + proxyDecorator: _proxyDecorator); + } + Widget _buildChips() { final i18n = AppLocalizations.of(context)!; var list = [ @@ -118,6 +184,7 @@ class _TaskManagerPage extends State floating: true, ), _buildChips(), + _buildList(context), ], ); }