feat(task): record and display task completion time

Add DoneAt to the download task model: set when a task finishes and
cleared when it is downloaded again. The value flows through the API
model and task record, and is shown as "Completed at" in the task
drawer info tab next to the creation time.
This commit is contained in:
Levi committed 2026-09-20 08:56:54 +08:00
1 parent 694b43e3f9
commit 4b231f52c5
30 files changed
+82 -23

No files matched your search

+2
View File
@@ -35,6 +35,7 @@ class Task {
Progress progress;
DateTime createdAt;
DateTime updatedAt;
DateTime? doneAt;
Task({
required this.id,
@@ -45,6 +46,7 @@ class Task {
required this.progress,
required this.createdAt,
required this.updatedAt,
this.doneAt,
});
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
+2
View File
@@ -15,6 +15,7 @@ Task _$TaskFromJson(Map<String, dynamic> json) => Task(
progress: Progress.fromJson(json['progress'] as Map<String, dynamic>),
createdAt: DateTime.parse(json['createdAt'] as String),
updatedAt: DateTime.parse(json['updatedAt'] as String),
doneAt: json['doneAt'] == null ? null : DateTime.parse(json['doneAt'] as String),
)..protocol = $enumDecodeNullable(_$ProtocolEnumMap, json['protocol']);
Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
@@ -27,6 +28,7 @@ Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
'progress': instance.progress.toJson(),
'createdAt': instance.createdAt.toIso8601String(),
'updatedAt': instance.updatedAt.toIso8601String(),
'doneAt': ?instance.doneAt?.toIso8601String(),
};
const _$StatusEnumMap = {
@@ -76,6 +76,7 @@ class TaskRecord {
this.remaining,
this.downloadDuration,
this.createdAt,
this.doneAt,
this.progress,
this.error,
this.completedLabel,
@@ -97,6 +98,7 @@ class TaskRecord {
final String? remaining;
final Duration? downloadDuration;
final DateTime? createdAt;
final DateTime? doneAt;
final double? progress;
final String url;
final String storagePath;
@@ -145,6 +147,7 @@ class TaskRecord {
remainingSeconds: _remainingSeconds(task, totalBytes, downloadedBytes),
downloadDuration: task.progress.used > 0 ? Duration(microseconds: (task.progress.used + 999) ~/ 1000) : null,
createdAt: task.createdAt,
doneAt: task.doneAt,
waiting: task.status == api_task.Status.wait,
progress: progress,
url: displayUrl,
@@ -319,6 +319,8 @@ class _TaskInfoTab extends StatelessWidget {
_InfoBlock(label: context.l10n.remaining, value: remaining),
if (task.createdAt case final createdAt?)
_InfoBlock(label: context.l10n.createdAt, value: _formatTaskDateTime(createdAt)),
if (task.doneAt case final doneAt?)
_InfoBlock(label: context.l10n.doneAt, value: _formatTaskDateTime(doneAt)),
Container(
key: const ValueKey('task-details-info-divider'),
height: 1,
+1
View File
@@ -246,6 +246,7 @@
"refresh": "تحديث",
"status": "الحالة",
"createdAt": "وقت الإنشاء",
"doneAt": "وقت الإكتمال",
"speed": "السرعة",
"uploadSpeed": "سرعة التشغيل",
"remaining": "البقايا",
+1
View File
@@ -260,6 +260,7 @@
"refresh": "Yenilə",
"status": "Vəziyyət",
"createdAt": "Yaradılıb",
"doneAt": "Tamamlanıb",
"speed": "Sürət",
"uploadSpeed": "Yükləmə sürəti",
"remaining": "Qalıb",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Refresca",
"status": "Estat",
"createdAt": "Data de creació",
"doneAt": "Data de finalització",
"speed": "Velocitat",
"uploadSpeed": "Velocitat de pujada",
"remaining": "Restant",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Aktualisieren",
"status": "-Status",
"createdAt": "Erstellungszeit",
"doneAt": "Abgeschlossen am",
"speed": "Geschwindigkeit",
"uploadSpeed": "Upload-Geschwindigkeit",
"remaining": "Verbleibend",
+1
View File
@@ -268,6 +268,7 @@
"refresh": "Refresh",
"status": "Status",
"createdAt": "Created",
"doneAt": "Completed at",
"speed": "Speed",
"uploadSpeed": "Upload speed",
"remaining": "Remaining",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Actualizar",
"status": "Situación",
"createdAt": "Fecha de creación",
"doneAt": "Fecha de finalización",
"speed": "Velocidad",
"uploadSpeed": "Velocidad de carga",
"remaining": "Permaneciendo",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "تازه‌سازی",
"status": "وضعیت",
"createdAt": "زمان ایجاد",
"doneAt": "زمان تکمیل",
"speed": "سرعت",
"uploadSpeed": "سرعت آپلود",
"remaining": "باقی مانده",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Rafraîchir",
"status": "État",
"createdAt": "Date de création",
"doneAt": "Terminé le",
"speed": "Vitesse",
"uploadSpeed": "Vitesse de chargement",
"remaining": "Reste",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Frissítés",
"status": "állapot",
"createdAt": "Létrehozás ideje",
"doneAt": "Befejezés ideje",
"speed": "sebesség",
"uploadSpeed": "Feltöltési sebesség",
"remaining": "Maradt",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Segarkan",
"status": "Status",
"createdAt": "Waktu dibuat",
"doneAt": "Waktu selesai",
"speed": "Kecepatan",
"uploadSpeed": "Kecepatan pemutakhiran",
"remaining": "Sisa",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Aggiorna",
"status": "Stato",
"createdAt": "Data di creazione",
"doneAt": "Data di completamento",
"speed": "Velocità",
"uploadSpeed": "Velocità di carico",
"remaining": "Rimanere",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "リフレッシュ",
"status": "ステータス",
"createdAt": "作成日時",
"doneAt": "完了日時",
"speed": "スピード",
"uploadSpeed": "アップロード速度",
"remaining": "メンテナンス",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "의붓기",
"status": "상태",
"createdAt": "생성 시간",
"doneAt": "완료 시간",
"speed": "속도",
"uploadSpeed": "연결 속도",
"remaining": "의논하기",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Odśwież",
"status": "Stan",
"createdAt": "Czas utworzenia",
"doneAt": "Czas ukończenia",
"speed": "Prędkość",
"uploadSpeed": "Prędkość wysyłania",
"remaining": "Pozostały",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Actualizar",
"status": "Estado",
"createdAt": "Data de criação",
"doneAt": "Data de conclusão",
"speed": "Velocidade",
"uploadSpeed": "Velocidade de envio",
"remaining": "Restante",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "освежиться",
"status": "Статус",
"createdAt": "Время создания",
"doneAt": "Время завершения",
"speed": "Скорость",
"uploadSpeed": "Скорость загрузки",
"remaining": "оставаться",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "புதுப்பி",
"status": "நிலை",
"createdAt": "உருவாக்கிய நேரம்",
"doneAt": "முடிந்த நேரம்",
"speed": "வேகம்",
"uploadSpeed": "வேகம்",
"remaining": "மீதம்",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Yenile",
"status": "Durum durumu",
"createdAt": "Oluşturulma zamanı",
"doneAt": "Tamamlanma zamanı",
"speed": "Hız",
"uploadSpeed": "Yük hızı",
"remaining": "Kalanlar",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Реверс",
"status": "Контакты",
"createdAt": "Час створення",
"doneAt": "Час завершення",
"speed": "ОДЯГ",
"uploadSpeed": "Швидкість завантаження",
"remaining": "Ременування",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "Cập nhật",
"status": "Trạng thái",
"createdAt": "Thời gian tạo",
"doneAt": "Thời gian hoàn thành",
"speed": "Tốc độ",
"uploadSpeed": "Tốc độ",
"remaining": "Ở lại",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "刷新",
"status": "状态",
"createdAt": "创建时间",
"doneAt": "完成时间",
"speed": "速度",
"uploadSpeed": "上传速度",
"remaining": "剩余时间",
+1
View File
@@ -246,6 +246,7 @@
"refresh": "重新整理",
"status": "狀態",
"createdAt": "建立時間",
"doneAt": "完成時間",
"speed": "速度",
"uploadSpeed": "上傳速度",
"remaining": "剩餘時間",