From c9470ef75177a46496432d3a1427280ff1c64dc3 Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 26 Jul 2026 17:39:28 +0700 Subject: [PATCH] feat(queue): add download-next action on queued items Queued rows get a skip-next button that reinserts the item ahead of all other queued items, so the next free slot picks it up. During an active native worker run the current batch keeps its order; the new order applies from the next run. --- lib/l10n/app_localizations.dart | 6 ++++ lib/l10n/app_localizations_ar.dart | 3 ++ lib/l10n/app_localizations_de.dart | 3 ++ lib/l10n/app_localizations_en.dart | 3 ++ lib/l10n/app_localizations_es.dart | 3 ++ lib/l10n/app_localizations_fr.dart | 3 ++ lib/l10n/app_localizations_hi.dart | 3 ++ lib/l10n/app_localizations_id.dart | 3 ++ lib/l10n/app_localizations_ja.dart | 3 ++ lib/l10n/app_localizations_ko.dart | 3 ++ lib/l10n/app_localizations_nl.dart | 3 ++ lib/l10n/app_localizations_pt.dart | 3 ++ lib/l10n/app_localizations_ru.dart | 3 ++ lib/l10n/app_localizations_tr.dart | 3 ++ lib/l10n/app_localizations_uk.dart | 3 ++ lib/l10n/app_localizations_zh.dart | 3 ++ lib/l10n/arb/app_en.arb | 4 +++ lib/providers/download_queue_provider.dart | 20 ++++++++++++ lib/screens/queue_tab_item_widgets.dart | 36 +++++++++++++++++----- 19 files changed, 103 insertions(+), 8 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index ed589607..efe0fee1 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -6220,6 +6220,12 @@ abstract class AppLocalizations { /// **'Finalizing download'** String get queueFinalizingDownload; + /// Tooltip on a queued download row; moves the item to the front of the queue so the next free slot downloads it + /// + /// In en, this message translates to: + /// **'Download next'** + String get queueDownloadNext; + /// Accessibility label when a downloaded file is missing from disk /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 9b70a5fc..421d24a6 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -3739,6 +3739,9 @@ class AppLocalizationsAr extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index d9ef00f9..29818e79 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -3785,6 +3785,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get queueFinalizingDownload => 'Download wird abgeschlossen'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Heruntergeladene Datei fehlt'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c0110ccc..60276f4d 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -3739,6 +3739,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 77bf9c6f..1b91ff23 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -3733,6 +3733,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index a719fac8..afd89e3a 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -3839,6 +3839,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get queueFinalizingDownload => 'Téléchargement en cours'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Fichier téléchargé manquant'; diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index 225479f2..c538b0d4 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -3739,6 +3739,9 @@ class AppLocalizationsHi extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 687692f0..6680a237 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -3742,6 +3742,9 @@ class AppLocalizationsId extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 105eb39f..b4a6cc18 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -3728,6 +3728,9 @@ class AppLocalizationsJa extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 642f7099..f93ff46e 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -3630,6 +3630,9 @@ class AppLocalizationsKo extends AppLocalizations { @override String get queueFinalizingDownload => '다운로드를 마무리하는 중'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => '다운로드한 파일이 없음'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index f3e2f081..6426b901 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -3739,6 +3739,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 359f286d..c00a0dfe 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -3733,6 +3733,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 7a3cc7c1..1c0759aa 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -3770,6 +3770,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 7c7e553d..dd8a7636 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -3769,6 +3769,9 @@ class AppLocalizationsTr extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index e1f67601..d7d012c2 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -3787,6 +3787,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 487305c9..bff28bbc 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -3733,6 +3733,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get queueFinalizingDownload => 'Finalizing download'; + @override + String get queueDownloadNext => 'Download next'; + @override String get queueDownloadedFileMissing => 'Downloaded file missing'; diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index a22b9cb9..a843e79b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -4845,6 +4845,10 @@ "@queueFinalizingDownload": { "description": "Accessibility label for a queue item that is finalizing" }, + "queueDownloadNext": "Download next", + "@queueDownloadNext": { + "description": "Tooltip on a queued download row; moves the item to the front of the queue so the next free slot downloads it" + }, "queueDownloadedFileMissing": "Downloaded file missing", "@queueDownloadedFileMissing": { "description": "Accessibility label when a downloaded file is missing from disk" diff --git a/lib/providers/download_queue_provider.dart b/lib/providers/download_queue_provider.dart index ddda39e7..8b8112f9 100644 --- a/lib/providers/download_queue_provider.dart +++ b/lib/providers/download_queue_provider.dart @@ -1670,6 +1670,26 @@ class DownloadQueueNotifier extends Notifier { } } + /// Reinserts a queued item ahead of all other queued items so the next + /// free download slot picks it up. During an active native worker run the + /// current batch keeps its order; the new order applies from the next run. + void downloadNext(String id) { + final items = state.items; + final index = items.indexWhere((item) => item.id == id); + if (index == -1) return; + final item = items[index]; + if (item.status != DownloadStatus.queued) return; + final firstQueuedIndex = items.indexWhere( + (candidate) => candidate.status == DownloadStatus.queued, + ); + if (firstQueuedIndex == index) return; + final reordered = List.from(items) + ..removeAt(index) + ..insert(firstQueuedIndex, item); + state = state.copyWith(items: reordered); + _saveQueueToStorage(); + } + void removeItem(String id) { final removedItem = state.items.where((item) => item.id == id).firstOrNull; _locallyCancelledItemIds.remove(id); diff --git a/lib/screens/queue_tab_item_widgets.dart b/lib/screens/queue_tab_item_widgets.dart index 8e66cd10..6b3f6a3e 100644 --- a/lib/screens/queue_tab_item_widgets.dart +++ b/lib/screens/queue_tab_item_widgets.dart @@ -486,14 +486,34 @@ extension _QueueTabItemWidgets on _QueueTabState { ) { switch (item.status) { case DownloadStatus.queued: - return IconButton( - onPressed: () => - ref.read(downloadQueueProvider.notifier).cancelItem(item.id), - icon: Icon(Icons.close, color: colorScheme.error), - tooltip: context.l10n.dialogCancel, - style: IconButton.styleFrom( - backgroundColor: colorScheme.errorContainer.withValues(alpha: 0.3), - ), + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: () => ref + .read(downloadQueueProvider.notifier) + .downloadNext(item.id), + icon: Icon(Icons.skip_next, color: colorScheme.primary), + tooltip: context.l10n.queueDownloadNext, + style: IconButton.styleFrom( + backgroundColor: colorScheme.primaryContainer.withValues( + alpha: 0.3, + ), + ), + ), + const SizedBox(width: 4), + IconButton( + onPressed: () => + ref.read(downloadQueueProvider.notifier).cancelItem(item.id), + icon: Icon(Icons.close, color: colorScheme.error), + tooltip: context.l10n.dialogCancel, + style: IconButton.styleFrom( + backgroundColor: colorScheme.errorContainer.withValues( + alpha: 0.3, + ), + ), + ), + ], ); case DownloadStatus.downloading: return IconButton(