feat(library): add Clear Pending action to transfer queue (#4617)
* feat(library): add Clear Pending action to transfer queue Adds a "Clear Pending" button to the transfer queue panel that removes only pending (including retry-pending) transfers, leaving in-progress, completed, failed, and cancelled items intact. Wired through the store, manager (with queue persistence), and useTransferQueue hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * i18n: translate Clear Pending and reading-statistics strings across locales Adds translations for "Clear Pending" (transfer queue) and the two reading-statistics sync-category strings ("Reading statistics" and its description) across all 33 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1886,6 +1886,7 @@
|
||||
"Privacy": "الخصوصية",
|
||||
"No downloadable format available": "لا يتوفر تنسيق قابل للتنزيل",
|
||||
"Content is neither valid XML nor JSON": "المحتوى ليس بصيغة XML ولا JSON صالحة",
|
||||
"Clear Pending": "مسح قيد الانتظار",
|
||||
"Reading statistics": "إحصائيات القراءة",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "وقت القراءة والصفحات المقروءة، تتم مزامنتها عبر أجهزتك و KOReader.",
|
||||
"Auto Sync": "مزامنة تلقائية"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "গোপনীয়তা",
|
||||
"No downloadable format available": "ডাউনলোডযোগ্য কোনো ফরম্যাট নেই",
|
||||
"Content is neither valid XML nor JSON": "বিষয়বস্তু বৈধ XML বা JSON কোনোটিই নয়",
|
||||
"Clear Pending": "অপেক্ষমাণ মুছুন",
|
||||
"Reading statistics": "পড়ার পরিসংখ্যান",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "পড়ার সময় এবং পড়া পৃষ্ঠা, আপনার ডিভাইস এবং KOReader জুড়ে সিঙ্ক করা হয়।",
|
||||
"Auto Sync": "স্বয়ংক্রিয় সিঙ্ক"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "གསང་དོན།",
|
||||
"No downloadable format available": "ཕབ་ལེན་བྱེད་ཆོག་པའི་རྣམ་པ་མི་འདུག",
|
||||
"Content is neither valid XML nor JSON": "ནང་དོན་ནི་ XML དང་ JSON གང་ཡང་ཚད་ལྡན་མིན།",
|
||||
"Clear Pending": "སྒུག་བཞིན་པ་གསལ་བ་",
|
||||
"Reading statistics": "ཀློག་འདོན་གྱི་གྲངས་ཐོ།",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "ཀློག་འདོན་གྱི་དུས་ཚོད་དང་ཀློག་ཟིན་པའི་ཤོག་གྲངས། ཁྱེད་ཀྱི་སྒྲིག་ཆས་དང་ KOReader བར་མཉམ་སྒྲིག་བྱེད།",
|
||||
"Auto Sync": "རང་འགུལ་སྤྲི་དོན།"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Datenschutz",
|
||||
"No downloadable format available": "Kein herunterladbares Format verfügbar",
|
||||
"Content is neither valid XML nor JSON": "Inhalt ist weder gültiges XML noch JSON",
|
||||
"Clear Pending": "Ausstehende löschen",
|
||||
"Reading statistics": "Lesestatistik",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Lesezeit und gelesene Seiten, synchronisiert über deine Geräte und KOReader.",
|
||||
"Auto Sync": "Automatische Synchronisierung"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Απόρρητο",
|
||||
"No downloadable format available": "Δεν υπάρχει διαθέσιμη μορφή για λήψη",
|
||||
"Content is neither valid XML nor JSON": "Το περιεχόμενο δεν είναι ούτε έγκυρο XML ούτε JSON",
|
||||
"Clear Pending": "Εκκαθάριση σε αναμονή",
|
||||
"Reading statistics": "Στατιστικά ανάγνωσης",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Χρόνος ανάγνωσης και σελίδες που διαβάστηκαν, συγχρονισμένα στις συσκευές σας και το KOReader.",
|
||||
"Auto Sync": "Αυτόματος συγχρονισμός"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Privacidad",
|
||||
"No downloadable format available": "No hay ningún formato descargable disponible",
|
||||
"Content is neither valid XML nor JSON": "El contenido no es XML ni JSON válido",
|
||||
"Clear Pending": "Limpiar pendientes",
|
||||
"Reading statistics": "Estadísticas de lectura",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Tiempo de lectura y páginas leídas, sincronizados entre tus dispositivos y KOReader.",
|
||||
"Auto Sync": "Sincronización automática"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "حریم خصوصی",
|
||||
"No downloadable format available": "هیچ قالب قابل دانلودی موجود نیست",
|
||||
"Content is neither valid XML nor JSON": "محتوا نه XML معتبر است و نه JSON",
|
||||
"Clear Pending": "پاک کردن در انتظارها",
|
||||
"Reading statistics": "آمار مطالعه",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "زمان مطالعه و صفحات خواندهشده، همگامسازیشده در دستگاههای شما و KOReader.",
|
||||
"Auto Sync": "همگامسازی خودکار"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Confidentialité",
|
||||
"No downloadable format available": "Aucun format téléchargeable disponible",
|
||||
"Content is neither valid XML nor JSON": "Le contenu n'est ni du XML ni du JSON valide",
|
||||
"Clear Pending": "Effacer les en attente",
|
||||
"Reading statistics": "Statistiques de lecture",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Temps de lecture et pages lues, synchronisés sur vos appareils et KOReader.",
|
||||
"Auto Sync": "Synchronisation automatique"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "פרטיות",
|
||||
"No downloadable format available": "אין פורמט להורדה זמין",
|
||||
"Content is neither valid XML nor JSON": "התוכן אינו XML או JSON תקין",
|
||||
"Clear Pending": "נקה בהמתנה",
|
||||
"Reading statistics": "סטטיסטיקת קריאה",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "זמן קריאה ועמודים שנקראו, מסונכרנים בין המכשירים שלך ו-KOReader.",
|
||||
"Auto Sync": "סנכרון אוטומטי"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "गोपनीयता",
|
||||
"No downloadable format available": "कोई डाउनलोड करने योग्य प्रारूप उपलब्ध नहीं है",
|
||||
"Content is neither valid XML nor JSON": "सामग्री न तो मान्य XML है और न ही JSON",
|
||||
"Clear Pending": "लंबित साफ़ करें",
|
||||
"Reading statistics": "पठन सांख्यिकी",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "पढ़ने का समय और पढ़े गए पृष्ठ, आपके डिवाइसों और KOReader के बीच सिंक किए गए।",
|
||||
"Auto Sync": "स्वतः सिंक"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Adatvédelem",
|
||||
"No downloadable format available": "Nincs letölthető formátum",
|
||||
"Content is neither valid XML nor JSON": "A tartalom sem érvényes XML, sem JSON",
|
||||
"Clear Pending": "Függőben lévők törlése",
|
||||
"Reading statistics": "Olvasási statisztika",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Olvasási idő és elolvasott oldalak, szinkronizálva az eszközei és a KOReader között.",
|
||||
"Auto Sync": "Automatikus szinkronizálás"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "Privasi",
|
||||
"No downloadable format available": "Tidak ada format yang dapat diunduh",
|
||||
"Content is neither valid XML nor JSON": "Konten bukan XML maupun JSON yang valid",
|
||||
"Clear Pending": "Hapus Menunggu",
|
||||
"Reading statistics": "Statistik Bacaan",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Waktu membaca dan halaman yang dibaca, disinkronkan di seluruh perangkat Anda dan KOReader.",
|
||||
"Auto Sync": "Sinkronisasi Otomatis"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Privacy",
|
||||
"No downloadable format available": "Nessun formato scaricabile disponibile",
|
||||
"Content is neither valid XML nor JSON": "Il contenuto non è né XML né JSON valido",
|
||||
"Clear Pending": "Cancella in attesa",
|
||||
"Reading statistics": "Statistiche di lettura",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Tempo di lettura e pagine lette, sincronizzati tra i tuoi dispositivi e KOReader.",
|
||||
"Auto Sync": "Sincronizzazione automatica"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "プライバシー",
|
||||
"No downloadable format available": "ダウンロード可能な形式がありません",
|
||||
"Content is neither valid XML nor JSON": "コンテンツが有効な XML でも JSON でもありません",
|
||||
"Clear Pending": "待機中を消去",
|
||||
"Reading statistics": "読書統計",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "読書時間と読んだページ数。デバイス間および KOReader と同期されます。",
|
||||
"Auto Sync": "自動同期"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "개인정보 보호",
|
||||
"No downloadable format available": "다운로드 가능한 형식이 없습니다",
|
||||
"Content is neither valid XML nor JSON": "콘텐츠가 유효한 XML 또는 JSON이 아닙니다",
|
||||
"Clear Pending": "대기 중 항목 지우기",
|
||||
"Reading statistics": "독서 통계",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "읽은 시간과 읽은 페이지 수로, 여러 기기와 KOReader 간에 동기화됩니다.",
|
||||
"Auto Sync": "자동 동기화"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "Privasi",
|
||||
"No downloadable format available": "Tiada format yang boleh dimuat turun",
|
||||
"Content is neither valid XML nor JSON": "Kandungan bukan XML mahupun JSON yang sah",
|
||||
"Clear Pending": "Kosongkan Menunggu",
|
||||
"Reading statistics": "Statistik Bacaan",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Masa membaca dan halaman dibaca, disegerakkan merentas peranti anda dan KOReader.",
|
||||
"Auto Sync": "Segerak Automatik"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Privacy",
|
||||
"No downloadable format available": "Geen downloadbaar formaat beschikbaar",
|
||||
"Content is neither valid XML nor JSON": "Inhoud is geen geldige XML of JSON",
|
||||
"Clear Pending": "Wachtende wissen",
|
||||
"Reading statistics": "Leesstatistieken",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Leestijd en gelezen pagina's, gesynchroniseerd op je apparaten en KOReader.",
|
||||
"Auto Sync": "Automatische synchronisatie"
|
||||
|
||||
@@ -1820,6 +1820,7 @@
|
||||
"Privacy": "Prywatność",
|
||||
"No downloadable format available": "Brak formatu do pobrania",
|
||||
"Content is neither valid XML nor JSON": "Treść nie jest prawidłowym XML ani JSON",
|
||||
"Clear Pending": "Wyczyść oczekujące",
|
||||
"Reading statistics": "Statystyki czytania",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Czas czytania i przeczytane strony, synchronizowane między Twoimi urządzeniami a KOReader.",
|
||||
"Auto Sync": "Automatyczna synchronizacja"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Privacidade",
|
||||
"No downloadable format available": "Nenhum formato disponível para download",
|
||||
"Content is neither valid XML nor JSON": "O conteúdo não é XML nem JSON válido",
|
||||
"Clear Pending": "Limpar pendentes",
|
||||
"Reading statistics": "Estatísticas de leitura",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Tempo de leitura e páginas lidas, sincronizados entre seus dispositivos e o KOReader.",
|
||||
"Auto Sync": "Sincronização automática"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Privacidade",
|
||||
"No downloadable format available": "Nenhum formato disponível para download",
|
||||
"Content is neither valid XML nor JSON": "O conteúdo não é XML nem JSON válido",
|
||||
"Clear Pending": "Limpar Pendentes",
|
||||
"Reading statistics": "Estatísticas de leitura",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Tempo de leitura e páginas lidas, sincronizados entre os seus dispositivos e o KOReader.",
|
||||
"Auto Sync": "Sincronização Automática"
|
||||
|
||||
@@ -1787,6 +1787,7 @@
|
||||
"Privacy": "Confidențialitate",
|
||||
"No downloadable format available": "Niciun format descărcabil disponibil",
|
||||
"Content is neither valid XML nor JSON": "Conținutul nu este nici XML, nici JSON valid",
|
||||
"Clear Pending": "Șterge în așteptare",
|
||||
"Reading statistics": "Statistici de lectură",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Timpul de citire și paginile citite, sincronizate pe dispozitivele tale și KOReader.",
|
||||
"Auto Sync": "Sincronizare automată"
|
||||
|
||||
@@ -1820,6 +1820,7 @@
|
||||
"Privacy": "Конфиденциальность",
|
||||
"No downloadable format available": "Нет доступного формата для загрузки",
|
||||
"Content is neither valid XML nor JSON": "Содержимое не является ни корректным XML, ни JSON",
|
||||
"Clear Pending": "Очистить ожидающие",
|
||||
"Reading statistics": "Статистика чтения",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Время чтения и прочитанные страницы, синхронизируются между вашими устройствами и KOReader.",
|
||||
"Auto Sync": "Автосинхронизация"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "පෞද්ගලිකත්වය",
|
||||
"No downloadable format available": "බාගත කළ හැකි ආකෘතියක් නොමැත",
|
||||
"Content is neither valid XML nor JSON": "අන්තර්ගතය වලංගු XML හෝ JSON නොවේ",
|
||||
"Clear Pending": "බලාපොරොත්තුවේ ඇති ඒවා මකන්න",
|
||||
"Reading statistics": "කියවීමේ සංඛ්යාලේඛන",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "කියවීමේ කාලය සහ කියවූ පිටු, ඔබේ උපාංග සහ KOReader හරහා සමමුහුර්ත වේ.",
|
||||
"Auto Sync": "ස්වයංක්රීය සමමුහුර්තය"
|
||||
|
||||
@@ -1820,6 +1820,7 @@
|
||||
"Privacy": "Zasebnost",
|
||||
"No downloadable format available": "Na voljo ni nobene oblike za prenos",
|
||||
"Content is neither valid XML nor JSON": "Vsebina ni veljaven XML niti JSON",
|
||||
"Clear Pending": "Počisti čakajoče",
|
||||
"Reading statistics": "Statistika branja",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Čas branja in prebrane strani, sinhronizirano med vašimi napravami in KOReader.",
|
||||
"Auto Sync": "Samodejna sinhronizacija"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Integritet",
|
||||
"No downloadable format available": "Inget nedladdningsbart format tillgängligt",
|
||||
"Content is neither valid XML nor JSON": "Innehållet är varken giltig XML eller JSON",
|
||||
"Clear Pending": "Rensa väntande",
|
||||
"Reading statistics": "Lässtatistik",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Lästid och lästa sidor, synkroniserat mellan dina enheter och KOReader.",
|
||||
"Auto Sync": "Automatisk synkronisering"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "தனியுரிமை",
|
||||
"No downloadable format available": "பதிவிறக்கம் செய்யக்கூடிய வடிவம் எதுவும் இல்லை",
|
||||
"Content is neither valid XML nor JSON": "உள்ளடக்கம் சரியான XML அல்லது JSON அல்ல",
|
||||
"Clear Pending": "காத்திருப்பதை அழிக்கவும்",
|
||||
"Reading statistics": "வாசிப்பு புள்ளிவிவரங்கள்",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "வாசிப்பு நேரம் மற்றும் படித்த பக்கங்கள், உங்கள் சாதனங்கள் மற்றும் KOReader இடையே ஒத்திசைக்கப்படும்.",
|
||||
"Auto Sync": "தானியங்கு ஒத்திசைவு"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "ความเป็นส่วนตัว",
|
||||
"No downloadable format available": "ไม่มีรูปแบบที่ดาวน์โหลดได้",
|
||||
"Content is neither valid XML nor JSON": "เนื้อหาไม่ใช่ทั้ง XML และ JSON ที่ถูกต้อง",
|
||||
"Clear Pending": "ล้างที่รอดำเนินการ",
|
||||
"Reading statistics": "สถิติการอ่าน",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "เวลาในการอ่านและจำนวนหน้าที่อ่าน ซิงค์ข้ามอุปกรณ์ของคุณและ KOReader",
|
||||
"Auto Sync": "ซิงค์อัตโนมัติ"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Gizlilik",
|
||||
"No downloadable format available": "İndirilebilir biçim yok",
|
||||
"Content is neither valid XML nor JSON": "İçerik geçerli XML veya JSON değil",
|
||||
"Clear Pending": "Bekleyenleri Temizle",
|
||||
"Reading statistics": "Okuma İstatistikleri",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Okuma süresi ve okunan sayfalar, cihazlarınız ve KOReader arasında senkronize edilir.",
|
||||
"Auto Sync": "Otomatik Senkronizasyon"
|
||||
|
||||
@@ -1820,6 +1820,7 @@
|
||||
"Privacy": "Конфіденційність",
|
||||
"No downloadable format available": "Немає доступного формату для завантаження",
|
||||
"Content is neither valid XML nor JSON": "Вміст не є дійсним XML чи JSON",
|
||||
"Clear Pending": "Очистити очікувані",
|
||||
"Reading statistics": "Статистика читання",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Час читання та прочитані сторінки, синхронізуються між вашими пристроями та KOReader.",
|
||||
"Auto Sync": "Автоматична синхронізація"
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
"Privacy": "Maxfiylik",
|
||||
"No downloadable format available": "Yuklab olinadigan format mavjud emas",
|
||||
"Content is neither valid XML nor JSON": "Kontent yaroqli XML ham, JSON ham emas",
|
||||
"Clear Pending": "Kutilayotganlarni tozalash",
|
||||
"Reading statistics": "Oʻqish statistikasi",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Oʻqish vaqti va oʻqilgan sahifalar, qurilmalaringiz va KOReader oʻrtasida sinxronlanadi.",
|
||||
"Auto Sync": "Avtomatik sinxronlash"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "Quyền riêng tư",
|
||||
"No downloadable format available": "Không có định dạng nào để tải xuống",
|
||||
"Content is neither valid XML nor JSON": "Nội dung không phải XML hay JSON hợp lệ",
|
||||
"Clear Pending": "Xóa đang chờ",
|
||||
"Reading statistics": "Thống kê đọc",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "Thời gian đọc và số trang đã đọc, được đồng bộ trên các thiết bị của bạn và KOReader.",
|
||||
"Auto Sync": "Tự động đồng bộ"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "隐私",
|
||||
"No downloadable format available": "没有可下载的格式",
|
||||
"Content is neither valid XML nor JSON": "内容既不是有效的 XML 也不是 JSON",
|
||||
"Clear Pending": "清除等待中",
|
||||
"Reading statistics": "阅读统计",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "阅读时间和已读页数,在您的设备和 KOReader 之间同步。",
|
||||
"Auto Sync": "自动同步"
|
||||
|
||||
@@ -1721,6 +1721,7 @@
|
||||
"Privacy": "隱私",
|
||||
"No downloadable format available": "沒有可下載的格式",
|
||||
"Content is neither valid XML nor JSON": "內容既不是有效的 XML 也不是 JSON",
|
||||
"Clear Pending": "清除等待中",
|
||||
"Reading statistics": "閱讀統計",
|
||||
"Reading time and pages read, synced across your devices and KOReader.": "閱讀時間與已讀頁數,在您的裝置與 KOReader 之間同步。",
|
||||
"Auto Sync": "自動同步"
|
||||
|
||||
@@ -497,6 +497,36 @@ describe('TransferManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── clearPending ─────────────────────────────────────────────────
|
||||
describe('clearPending', () => {
|
||||
test('removes pending transfers, keeps others, and persists the queue', async () => {
|
||||
const book1 = makeBook({ hash: 'h1', title: 'B1' });
|
||||
const book2 = makeBook({ hash: 'h2', title: 'B2' });
|
||||
const appService = makeAppService();
|
||||
await transferManager.initialize(
|
||||
appService as never,
|
||||
() => [book1, book2],
|
||||
vi.fn(),
|
||||
translationFn,
|
||||
);
|
||||
|
||||
// Pause first so queued items stay pending instead of being processed.
|
||||
transferManager.pauseQueue();
|
||||
const id1 = transferManager.queueUpload(book1)!;
|
||||
const id2 = transferManager.queueDownload(book2)!;
|
||||
useTransferStore.getState().setTransferStatus(id2, 'completed');
|
||||
|
||||
transferManager.clearPending();
|
||||
|
||||
expect(useTransferStore.getState().transfers[id1]).toBeUndefined();
|
||||
expect(useTransferStore.getState().transfers[id2]).toBeDefined();
|
||||
|
||||
const stored = JSON.parse(localStorage.getItem('readest_transfer_queue')!);
|
||||
expect(stored.transfers[id1]).toBeUndefined();
|
||||
expect(stored.transfers[id2]).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Queue processing (integration-style) ─────────────────────────
|
||||
describe('queue processing', () => {
|
||||
test('successful upload calls appService.uploadBook and updates book', async () => {
|
||||
|
||||
@@ -226,6 +226,40 @@ describe('transferStore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── clearPending ─────────────────────────────────────────────────
|
||||
describe('clearPending', () => {
|
||||
test('removes only pending transfers', () => {
|
||||
const id1 = useTransferStore.getState().addTransfer('h1', 'B1', 'upload');
|
||||
const id2 = useTransferStore.getState().addTransfer('h2', 'B2', 'download');
|
||||
const id3 = useTransferStore.getState().addTransfer('h3', 'B3', 'upload');
|
||||
const id4 = useTransferStore.getState().addTransfer('h4', 'B4', 'delete');
|
||||
const id5 = useTransferStore.getState().addTransfer('h5', 'B5', 'upload');
|
||||
useTransferStore.getState().setTransferStatus(id1, 'in_progress');
|
||||
useTransferStore.getState().setTransferStatus(id2, 'completed');
|
||||
useTransferStore.getState().setTransferStatus(id3, 'failed', 'err');
|
||||
useTransferStore.getState().setTransferStatus(id4, 'cancelled');
|
||||
// id5 remains pending
|
||||
|
||||
useTransferStore.getState().clearPending();
|
||||
const transfers = useTransferStore.getState().transfers;
|
||||
expect(transfers[id1]).toBeDefined(); // in_progress kept
|
||||
expect(transfers[id2]).toBeDefined(); // completed kept
|
||||
expect(transfers[id3]).toBeDefined(); // failed kept
|
||||
expect(transfers[id4]).toBeDefined(); // cancelled kept
|
||||
expect(transfers[id5]).toBeUndefined(); // pending removed
|
||||
});
|
||||
|
||||
test('removes retry-pending transfers (status pending with an error message)', () => {
|
||||
const id = useTransferStore.getState().addTransfer('h', 'B', 'upload');
|
||||
// mimic the retry path: failed -> back to pending with a "Retry x/y" note
|
||||
useTransferStore.getState().setTransferStatus(id, 'pending', 'Retry 1/3');
|
||||
expect(useTransferStore.getState().transfers[id]!.status).toBe('pending');
|
||||
|
||||
useTransferStore.getState().clearPending();
|
||||
expect(useTransferStore.getState().transfers[id]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ── clearAll ─────────────────────────────────────────────────────
|
||||
describe('clearAll', () => {
|
||||
test('removes all transfers', () => {
|
||||
|
||||
@@ -166,6 +166,7 @@ const TransferQueuePanel: React.FC = () => {
|
||||
resumeQueue,
|
||||
clearCompleted,
|
||||
clearFailed,
|
||||
clearPending,
|
||||
queueUpload,
|
||||
queueDownload,
|
||||
} = useTransferQueue();
|
||||
@@ -335,6 +336,12 @@ const TransferQueuePanel: React.FC = () => {
|
||||
{_('Retry All')}
|
||||
</button>
|
||||
)}
|
||||
{stats.pending > 0 && (
|
||||
<button onClick={clearPending} className='btn btn-ghost btn-sm gap-1'>
|
||||
<MdDeleteSweep size={iconSize - 2} />
|
||||
{_('Clear Pending')}
|
||||
</button>
|
||||
)}
|
||||
{stats.completed > 0 && (
|
||||
<button onClick={clearCompleted} className='btn btn-ghost btn-sm gap-1'>
|
||||
<MdDeleteSweep size={iconSize - 2} />
|
||||
|
||||
@@ -74,6 +74,10 @@ export function useTransferQueue(libraryLoaded = true, delayInit = 0) {
|
||||
useTransferStore.getState().clearFailed();
|
||||
}, []);
|
||||
|
||||
const clearPending = useCallback(() => {
|
||||
transferManager.clearPending();
|
||||
}, []);
|
||||
|
||||
const clearAll = useCallback(() => {
|
||||
useTransferStore.getState().clearAll();
|
||||
}, []);
|
||||
@@ -136,6 +140,7 @@ export function useTransferQueue(libraryLoaded = true, delayInit = 0) {
|
||||
resumeQueue,
|
||||
clearCompleted,
|
||||
clearFailed,
|
||||
clearPending,
|
||||
clearAll,
|
||||
getTransferProgress,
|
||||
};
|
||||
|
||||
@@ -254,6 +254,11 @@ class TransferManager {
|
||||
this.persistQueue();
|
||||
}
|
||||
|
||||
clearPending(): void {
|
||||
useTransferStore.getState().clearPending();
|
||||
this.persistQueue();
|
||||
}
|
||||
|
||||
private async processQueue(): Promise<void> {
|
||||
if (this.isProcessing) return;
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ interface TransferState {
|
||||
resumeQueue: () => void;
|
||||
clearCompleted: () => void;
|
||||
clearFailed: () => void;
|
||||
clearPending: () => void;
|
||||
clearAll: () => void;
|
||||
|
||||
// Getters
|
||||
@@ -303,6 +304,18 @@ export const useTransferStore = create<TransferState>((set, get) => ({
|
||||
});
|
||||
},
|
||||
|
||||
clearPending: () => {
|
||||
set((state) => {
|
||||
const remaining: Record<string, TransferItem> = {};
|
||||
Object.entries(state.transfers).forEach(([id, transfer]) => {
|
||||
if (transfer.status !== 'pending') {
|
||||
remaining[id] = transfer;
|
||||
}
|
||||
});
|
||||
return { transfers: remaining };
|
||||
});
|
||||
},
|
||||
|
||||
clearAll: () => set({ transfers: {} }),
|
||||
|
||||
getPendingTransfers: () => {
|
||||
|
||||
Reference in New Issue
Block a user