From d5c02e6253ecfa4b83cf8d6afc12e1a995369d84 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Wed, 17 Jun 2026 12:28:21 +0800 Subject: [PATCH] feat(library): add Purge Data and fold detail actions into a More menu (#4615) (#4626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #4615. Re-importing updated serials left the app-generated Books// folder (config.json reading progress/notes, nav.json, cover) on disk after a normal delete, forcing a manual cleanup. "Purge Data" now does a Cloud & Device delete AND wipes the whole directory in one action. The book detail action row is redesigned to Edit · Download · Upload · Delete · More (hamburger): - Goodreads, Share, and Export move into the hamburger "More" menu. - Share is enabled only when signed in and the local file exists; Export is enabled when the local file exists (kept on every platform since the bottom-bar Send is mobile/macOS-only). - Purge Data is the red entry in the Delete menu, behind a strong confirm. Implementation: - DeleteAction gains 'purge'; cloudService.deleteBook('purge') removes the in-place source file and removeDir's the whole Books// folder, clearing downloadedAt and leaving the tombstone + queued cloud delete to the page (mirrors 'both'/'local'). - The library page wires handleBookDelete('purge'); BookDetailModal adds the purge confirm config + share/export handlers and gates Share on auth. Tests: cloud-service purge cases, BookDetailView More-menu + Purge tests. i18n: 9 new keys translated across all 33 locales. Co-authored-by: Claude Opus 4.8 (1M context) --- .../public/locales/ar/translation.json | 11 +- .../public/locales/bn/translation.json | 11 +- .../public/locales/bo/translation.json | 11 +- .../public/locales/de/translation.json | 11 +- .../public/locales/el/translation.json | 11 +- .../public/locales/es/translation.json | 11 +- .../public/locales/fa/translation.json | 11 +- .../public/locales/fr/translation.json | 11 +- .../public/locales/he/translation.json | 11 +- .../public/locales/hi/translation.json | 11 +- .../public/locales/hu/translation.json | 11 +- .../public/locales/id/translation.json | 11 +- .../public/locales/it/translation.json | 11 +- .../public/locales/ja/translation.json | 11 +- .../public/locales/ko/translation.json | 11 +- .../public/locales/ms/translation.json | 11 +- .../public/locales/nl/translation.json | 11 +- .../public/locales/pl/translation.json | 11 +- .../public/locales/pt-BR/translation.json | 11 +- .../public/locales/pt/translation.json | 11 +- .../public/locales/ro/translation.json | 11 +- .../public/locales/ru/translation.json | 11 +- .../public/locales/si/translation.json | 11 +- .../public/locales/sl/translation.json | 11 +- .../public/locales/sv/translation.json | 11 +- .../public/locales/ta/translation.json | 11 +- .../public/locales/th/translation.json | 11 +- .../public/locales/tr/translation.json | 11 +- .../public/locales/uk/translation.json | 11 +- .../public/locales/uz/translation.json | 11 +- .../public/locales/vi/translation.json | 11 +- .../public/locales/zh-CN/translation.json | 11 +- .../public/locales/zh-TW/translation.json | 11 +- .../components/BookDetailModal.test.tsx | 4 + .../components/BookDetailView.test.tsx | 83 +++++++++++++++ .../__tests__/services/cloud-service.test.ts | 67 ++++++++++++ apps/readest-app/src/app/library/page.tsx | 15 ++- .../components/metadata/BookDetailModal.tsx | 50 +++++++-- .../components/metadata/BookDetailView.tsx | 100 ++++++++++++++---- apps/readest-app/src/services/cloudService.ts | 22 +++- apps/readest-app/src/types/system.ts | 2 +- 41 files changed, 630 insertions(+), 76 deletions(-) diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 61921fb3..83423088 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -1889,5 +1889,14 @@ "Clear Pending": "مسح قيد الانتظار", "Reading statistics": "إحصائيات القراءة", "Reading time and pages read, synced across your devices and KOReader.": "وقت القراءة والصفحات المقروءة، تتم مزامنتها عبر أجهزتك و KOReader.", - "Auto Sync": "مزامنة تلقائية" + "Auto Sync": "مزامنة تلقائية", + "Purged book data: {{title}}": "تم محو بيانات الكتاب: {{title}}", + "Failed to purge book data: {{title}}": "فشل محو بيانات الكتاب: {{title}}", + "Purge Book Data": "محو جميع بيانات الكتاب", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "سيؤدي هذا إلى محو الكتاب وجميع بياناته نهائيًا، بما في ذلك تقدم القراءة والملاحظات والإشارات المرجعية. لا يمكن التراجع عن هذا الإجراء.", + "Purge Data": "محو جميع البيانات", + "Erase the book and all its data, including reading progress": "محو الكتاب وجميع بياناته، بما في ذلك تقدم القراءة", + "More Actions": "إجراءات أخرى", + "Sign in and make the book available to share it": "سجّل الدخول واجعل الكتاب متاحًا لمشاركته", + "Download the book to export it": "نزّل الكتاب لتصديره" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index 89e85565..7aafaf75 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "অপেক্ষমাণ মুছুন", "Reading statistics": "পড়ার পরিসংখ্যান", "Reading time and pages read, synced across your devices and KOReader.": "পড়ার সময় এবং পড়া পৃষ্ঠা, আপনার ডিভাইস এবং KOReader জুড়ে সিঙ্ক করা হয়।", - "Auto Sync": "স্বয়ংক্রিয় সিঙ্ক" + "Auto Sync": "স্বয়ংক্রিয় সিঙ্ক", + "Purged book data: {{title}}": "বইয়ের ডেটা মুছে ফেলা হয়েছে: {{title}}", + "Failed to purge book data: {{title}}": "বইয়ের ডেটা মুছতে ব্যর্থ: {{title}}", + "Purge Book Data": "বইয়ের সমস্ত ডেটা মুছুন", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "এটি বইটি এবং পড়ার অগ্রগতি, নোট ও বুকমার্ক সহ এর সমস্ত ডেটা স্থায়ীভাবে মুছে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না।", + "Purge Data": "সমস্ত ডেটা মুছুন", + "Erase the book and all its data, including reading progress": "পড়ার অগ্রগতিসহ বই ও এর সমস্ত ডেটা মুছে ফেলুন", + "More Actions": "আরও ক্রিয়া", + "Sign in and make the book available to share it": "শেয়ার করতে সাইন ইন করুন এবং বইটি উপলব্ধ করুন", + "Download the book to export it": "এক্সপোর্ট করতে বইটি ডাউনলোড করুন" } diff --git a/apps/readest-app/public/locales/bo/translation.json b/apps/readest-app/public/locales/bo/translation.json index 37aa361f..869064b1 100644 --- a/apps/readest-app/public/locales/bo/translation.json +++ b/apps/readest-app/public/locales/bo/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "སྒུག་བཞིན་པ་གསལ་བ་", "Reading statistics": "ཀློག་འདོན་གྱི་གྲངས་ཐོ།", "Reading time and pages read, synced across your devices and KOReader.": "ཀློག་འདོན་གྱི་དུས་ཚོད་དང་ཀློག་ཟིན་པའི་ཤོག་གྲངས། ཁྱེད་ཀྱི་སྒྲིག་ཆས་དང་ KOReader བར་མཉམ་སྒྲིག་བྱེད།", - "Auto Sync": "རང་འགུལ་སྤྲི་དོན།" + "Auto Sync": "རང་འགུལ་སྤྲི་དོན།", + "Purged book data: {{title}}": "དཔེ་དེབ་ཀྱི་གཞི་གྲངས་བསུབ་ཟིན། {{title}}", + "Failed to purge book data: {{title}}": "དཔེ་དེབ་ཀྱི་གཞི་གྲངས་བསུབ་མ་ཐུབ། {{title}}", + "Purge Book Data": "དཔེ་དེབ་ཀྱི་གཞི་གྲངས་ཡོངས་སུ་བསུབ།", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "འདིས་དཔེ་དེབ་དང་ཀློག་འདོན་གྱི་འཕེལ་རིམ། ཟིན་ཐོ། དེབ་རྟགས་བཅས་ཀྱི་གཞི་གྲངས་ཡོངས་རྫོགས་གཏན་འཁེལ་དུ་བསུབ་པ་ཡིན། འདི་ཕྱིར་བསྒྱུར་མི་ཐུབ།", + "Purge Data": "གཞི་གྲངས་ཡོངས་སུ་བསུབ།", + "Erase the book and all its data, including reading progress": "དཔེ་དེབ་དང་ཀློག་འདོན་གྱི་འཕེལ་རིམ་བཅས་ཀྱི་གཞི་གྲངས་ཡོངས་སུ་བསུབ།", + "More Actions": "བྱ་སྤྱོད་གཞན་དག", + "Sign in and make the book available to share it": "ནང་འཛུལ་བྱས་ནས་དཔེ་དེབ་མཁོ་སྤྲོད་རུང་བར་བཟོས་ནས་མཉམ་སྤྱོད་བྱོས།", + "Download the book to export it": "ཕྱིར་འདྲེན་བྱེད་པར་དཔེ་དེབ་ཕབ་ལེན་བྱོས།" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index c964ee9a..b85d08ba 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Automatische Synchronisierung", + "Purged book data: {{title}}": "Buchdaten gelöscht: {{title}}", + "Failed to purge book data: {{title}}": "Buchdaten konnten nicht gelöscht werden: {{title}}", + "Purge Book Data": "Buchdaten endgültig löschen", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Dadurch werden das Buch und alle zugehörigen Daten – einschließlich Lesefortschritt, Notizen und Lesezeichen – endgültig gelöscht. Dies kann nicht rückgängig gemacht werden.", + "Purge Data": "Alle Daten löschen", + "Erase the book and all its data, including reading progress": "Buch und alle Daten löschen, einschließlich Lesefortschritt", + "More Actions": "Weitere Aktionen", + "Sign in and make the book available to share it": "Melde dich an und mache das Buch verfügbar, um es zu teilen", + "Download the book to export it": "Lade das Buch herunter, um es zu exportieren" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 0aa3ba83..a959eeca 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "Εκκαθάριση σε αναμονή", "Reading statistics": "Στατιστικά ανάγνωσης", "Reading time and pages read, synced across your devices and KOReader.": "Χρόνος ανάγνωσης και σελίδες που διαβάστηκαν, συγχρονισμένα στις συσκευές σας και το KOReader.", - "Auto Sync": "Αυτόματος συγχρονισμός" + "Auto Sync": "Αυτόματος συγχρονισμός", + "Purged book data: {{title}}": "Τα δεδομένα του βιβλίου διαγράφηκαν: {{title}}", + "Failed to purge book data: {{title}}": "Αποτυχία διαγραφής δεδομένων βιβλίου: {{title}}", + "Purge Book Data": "Διαγραφή δεδομένων βιβλίου", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Αυτό διαγράφει οριστικά το βιβλίο και όλα τα δεδομένα του, συμπεριλαμβανομένης της προόδου ανάγνωσης, των σημειώσεων και των σελιδοδεικτών. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", + "Purge Data": "Διαγραφή όλων των δεδομένων", + "Erase the book and all its data, including reading progress": "Διαγράφει το βιβλίο και όλα τα δεδομένα του, συμπεριλαμβανομένης της προόδου ανάγνωσης", + "More Actions": "Περισσότερες ενέργειες", + "Sign in and make the book available to share it": "Συνδεθείτε και κάντε το βιβλίο διαθέσιμο για να το μοιραστείτε", + "Download the book to export it": "Κατεβάστε το βιβλίο για να το εξαγάγετε" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 93d28147..22852725 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -1790,5 +1790,14 @@ "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" + "Auto Sync": "Sincronización automática", + "Purged book data: {{title}}": "Datos del libro borrados: {{title}}", + "Failed to purge book data: {{title}}": "No se pudieron borrar los datos del libro: {{title}}", + "Purge Book Data": "Borrar todos los datos del libro", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Esto borra permanentemente el libro y todos sus datos, incluido el progreso de lectura, las notas y los marcadores. Esta acción no se puede deshacer.", + "Purge Data": "Borrar todos los datos", + "Erase the book and all its data, including reading progress": "Borra el libro y todos sus datos, incluido el progreso de lectura", + "More Actions": "Más acciones", + "Sign in and make the book available to share it": "Inicia sesión y ten el libro disponible para compartirlo", + "Download the book to export it": "Descarga el libro para exportarlo" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index c43b2cc3..1b7ad4f2 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "پاک کردن در انتظارها", "Reading statistics": "آمار مطالعه", "Reading time and pages read, synced across your devices and KOReader.": "زمان مطالعه و صفحات خوانده‌شده، همگام‌سازی‌شده در دستگاه‌های شما و KOReader.", - "Auto Sync": "همگام‌سازی خودکار" + "Auto Sync": "همگام‌سازی خودکار", + "Purged book data: {{title}}": "داده‌های کتاب پاک شد: {{title}}", + "Failed to purge book data: {{title}}": "پاک‌سازی داده‌های کتاب ناموفق بود: {{title}}", + "Purge Book Data": "پاک‌سازی داده‌های کتاب", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "این کار کتاب و تمام داده‌های آن، شامل پیشرفت مطالعه، یادداشت‌ها و نشانک‌ها را برای همیشه پاک می‌کند. این عمل قابل بازگشت نیست.", + "Purge Data": "پاک‌سازی داده‌ها", + "Erase the book and all its data, including reading progress": "کتاب و تمام داده‌های آن، شامل پیشرفت مطالعه را پاک می‌کند", + "More Actions": "اقدامات بیشتر", + "Sign in and make the book available to share it": "برای اشتراک‌گذاری، وارد شوید و کتاب را در دسترس قرار دهید", + "Download the book to export it": "برای خروجی گرفتن، کتاب را دانلود کنید" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 3fe889ff..ca126396 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -1790,5 +1790,14 @@ "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" + "Auto Sync": "Synchronisation automatique", + "Purged book data: {{title}}": "Données du livre purgées : {{title}}", + "Failed to purge book data: {{title}}": "Échec de la purge des données du livre : {{title}}", + "Purge Book Data": "Purger les données du livre", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Cette action supprime définitivement le livre et toutes ses données, y compris la progression de lecture, les notes et les signets. Cette action est irréversible.", + "Purge Data": "Purger les données", + "Erase the book and all its data, including reading progress": "Supprime le livre et toutes ses données, y compris la progression de lecture", + "More Actions": "Plus d'actions", + "Sign in and make the book available to share it": "Connectez-vous et rendez le livre disponible pour le partager", + "Download the book to export it": "Téléchargez le livre pour l'exporter" } diff --git a/apps/readest-app/public/locales/he/translation.json b/apps/readest-app/public/locales/he/translation.json index 2feff2ed..52b0c6b6 100644 --- a/apps/readest-app/public/locales/he/translation.json +++ b/apps/readest-app/public/locales/he/translation.json @@ -1790,5 +1790,14 @@ "Clear Pending": "נקה בהמתנה", "Reading statistics": "סטטיסטיקת קריאה", "Reading time and pages read, synced across your devices and KOReader.": "זמן קריאה ועמודים שנקראו, מסונכרנים בין המכשירים שלך ו-KOReader.", - "Auto Sync": "סנכרון אוטומטי" + "Auto Sync": "סנכרון אוטומטי", + "Purged book data: {{title}}": "נתוני הספר נמחקו: {{title}}", + "Failed to purge book data: {{title}}": "מחיקת נתוני הספר נכשלה: {{title}}", + "Purge Book Data": "מחיקת כל נתוני הספר", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "פעולה זו מוחקת לצמיתות את הספר ואת כל הנתונים שלו, כולל התקדמות הקריאה, ההערות והסימניות. לא ניתן לבטל פעולה זו.", + "Purge Data": "מחק את כל הנתונים", + "Erase the book and all its data, including reading progress": "מחיקת הספר וכל הנתונים שלו, כולל התקדמות הקריאה", + "More Actions": "פעולות נוספות", + "Sign in and make the book available to share it": "היכנס והפוך את הספר לזמין כדי לשתף אותו", + "Download the book to export it": "הורד את הספר כדי לייצא אותו" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index d1bcd6b4..1c2957be 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "लंबित साफ़ करें", "Reading statistics": "पठन सांख्यिकी", "Reading time and pages read, synced across your devices and KOReader.": "पढ़ने का समय और पढ़े गए पृष्ठ, आपके डिवाइसों और KOReader के बीच सिंक किए गए।", - "Auto Sync": "स्वतः सिंक" + "Auto Sync": "स्वतः सिंक", + "Purged book data: {{title}}": "पुस्तक का डेटा मिटाया गया: {{title}}", + "Failed to purge book data: {{title}}": "पुस्तक का डेटा मिटाने में विफल: {{title}}", + "Purge Book Data": "पुस्तक का सारा डेटा मिटाएँ", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "इससे पुस्तक और उसका सारा डेटा, जिसमें पढ़ने की प्रगति, नोट्स और बुकमार्क शामिल हैं, स्थायी रूप से मिट जाएगा। इसे पूर्ववत नहीं किया जा सकता।", + "Purge Data": "सारा डेटा मिटाएँ", + "Erase the book and all its data, including reading progress": "पढ़ने की प्रगति सहित पुस्तक और उसका सारा डेटा मिटाएँ", + "More Actions": "अधिक क्रियाएँ", + "Sign in and make the book available to share it": "साझा करने के लिए साइन इन करें और पुस्तक उपलब्ध कराएँ", + "Download the book to export it": "निर्यात करने के लिए पुस्तक डाउनलोड करें" } diff --git a/apps/readest-app/public/locales/hu/translation.json b/apps/readest-app/public/locales/hu/translation.json index 9f1e6f0d..09d1adb4 100644 --- a/apps/readest-app/public/locales/hu/translation.json +++ b/apps/readest-app/public/locales/hu/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Automatikus szinkronizálás", + "Purged book data: {{title}}": "Könyv adatai törölve: {{title}}", + "Failed to purge book data: {{title}}": "A könyv adatainak törlése sikertelen: {{title}}", + "Purge Book Data": "Könyv összes adatának törlése", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Ez véglegesen törli a könyvet és minden adatát, beleértve az olvasási előrehaladást, a jegyzeteket és a könyvjelzőket. A művelet nem vonható vissza.", + "Purge Data": "Összes adat törlése", + "Erase the book and all its data, including reading progress": "Törli a könyvet és minden adatát, beleértve az olvasási előrehaladást", + "More Actions": "További műveletek", + "Sign in and make the book available to share it": "Jelentkezz be, és tedd elérhetővé a könyvet a megosztáshoz", + "Download the book to export it": "Töltsd le a könyvet az exportáláshoz" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 30b13b0f..405532ee 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -1724,5 +1724,14 @@ "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" + "Auto Sync": "Sinkronisasi Otomatis", + "Purged book data: {{title}}": "Data buku dihapus: {{title}}", + "Failed to purge book data: {{title}}": "Gagal menghapus data buku: {{title}}", + "Purge Book Data": "Hapus semua data buku", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Ini akan menghapus buku secara permanen beserta semua datanya, termasuk progres baca, catatan, dan markah. Tindakan ini tidak dapat dibatalkan.", + "Purge Data": "Hapus semua data", + "Erase the book and all its data, including reading progress": "Menghapus buku dan semua datanya, termasuk progres baca", + "More Actions": "Tindakan lainnya", + "Sign in and make the book available to share it": "Masuk dan sediakan buku untuk membagikannya", + "Download the book to export it": "Unduh buku untuk mengekspornya" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index b208e768..19123d97 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -1790,5 +1790,14 @@ "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" + "Auto Sync": "Sincronizzazione automatica", + "Purged book data: {{title}}": "Dati del libro eliminati: {{title}}", + "Failed to purge book data: {{title}}": "Impossibile eliminare i dati del libro: {{title}}", + "Purge Book Data": "Elimina tutti i dati del libro", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Questa operazione elimina definitivamente il libro e tutti i suoi dati, inclusi i progressi di lettura, le note e i segnalibri. L'operazione non può essere annullata.", + "Purge Data": "Elimina tutti i dati", + "Erase the book and all its data, including reading progress": "Elimina il libro e tutti i suoi dati, inclusi i progressi di lettura", + "More Actions": "Altre azioni", + "Sign in and make the book available to share it": "Accedi e rendi disponibile il libro per condividerlo", + "Download the book to export it": "Scarica il libro per esportarlo" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 539bfdd4..e3586fee 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "待機中を消去", "Reading statistics": "読書統計", "Reading time and pages read, synced across your devices and KOReader.": "読書時間と読んだページ数。デバイス間および KOReader と同期されます。", - "Auto Sync": "自動同期" + "Auto Sync": "自動同期", + "Purged book data: {{title}}": "書籍データを消去しました: {{title}}", + "Failed to purge book data: {{title}}": "書籍データの消去に失敗しました: {{title}}", + "Purge Book Data": "書籍データを消去", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "これにより、本と読書の進行状況・メモ・しおりを含むすべてのデータが完全に削除されます。この操作は取り消せません。", + "Purge Data": "データを消去", + "Erase the book and all its data, including reading progress": "本と読書の進行状況を含むすべてのデータを消去します", + "More Actions": "その他の操作", + "Sign in and make the book available to share it": "共有するにはサインインして書籍を利用可能にしてください", + "Download the book to export it": "エクスポートするには書籍をダウンロードしてください" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index f50ed259..b6300283 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "대기 중 항목 지우기", "Reading statistics": "독서 통계", "Reading time and pages read, synced across your devices and KOReader.": "읽은 시간과 읽은 페이지 수로, 여러 기기와 KOReader 간에 동기화됩니다.", - "Auto Sync": "자동 동기화" + "Auto Sync": "자동 동기화", + "Purged book data: {{title}}": "도서 데이터를 삭제했습니다: {{title}}", + "Failed to purge book data: {{title}}": "도서 데이터 삭제에 실패했습니다: {{title}}", + "Purge Book Data": "도서 데이터 완전 삭제", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "책과 읽기 진행률, 메모, 책갈피를 포함한 모든 데이터가 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다.", + "Purge Data": "데이터 완전 삭제", + "Erase the book and all its data, including reading progress": "읽기 진행률을 포함한 책과 모든 데이터를 삭제합니다", + "More Actions": "추가 작업", + "Sign in and make the book available to share it": "공유하려면 로그인하고 책을 사용할 수 있도록 하세요", + "Download the book to export it": "내보내려면 책을 다운로드하세요" } diff --git a/apps/readest-app/public/locales/ms/translation.json b/apps/readest-app/public/locales/ms/translation.json index 49a89809..5395d798 100644 --- a/apps/readest-app/public/locales/ms/translation.json +++ b/apps/readest-app/public/locales/ms/translation.json @@ -1724,5 +1724,14 @@ "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" + "Auto Sync": "Segerak Automatik", + "Purged book data: {{title}}": "Data buku dipadam: {{title}}", + "Failed to purge book data: {{title}}": "Gagal memadam data buku: {{title}}", + "Purge Book Data": "Padam semua data buku", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Ini memadamkan buku dan semua datanya secara kekal, termasuk kemajuan bacaan, nota dan penanda buku. Tindakan ini tidak boleh dibuat asal.", + "Purge Data": "Padam semua data", + "Erase the book and all its data, including reading progress": "Memadam buku dan semua datanya, termasuk kemajuan bacaan", + "More Actions": "Tindakan lain", + "Sign in and make the book available to share it": "Log masuk dan sediakan buku untuk berkongsinya", + "Download the book to export it": "Muat turun buku untuk mengeksportnya" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index 975a7d3a..ced8581b 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Automatische synchronisatie", + "Purged book data: {{title}}": "Boekgegevens gewist: {{title}}", + "Failed to purge book data: {{title}}": "Kan boekgegevens niet wissen: {{title}}", + "Purge Book Data": "Alle boekgegevens wissen", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Hiermee worden het boek en alle bijbehorende gegevens, waaronder leesvoortgang, notities en bladwijzers, permanent gewist. Dit kan niet ongedaan worden gemaakt.", + "Purge Data": "Alle gegevens wissen", + "Erase the book and all its data, including reading progress": "Wist het boek en alle gegevens, inclusief leesvoortgang", + "More Actions": "Meer acties", + "Sign in and make the book available to share it": "Meld je aan en maak het boek beschikbaar om het te delen", + "Download the book to export it": "Download het boek om het te exporteren" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 863ce8de..44881dde 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -1823,5 +1823,14 @@ "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" + "Auto Sync": "Automatyczna synchronizacja", + "Purged book data: {{title}}": "Usunięto dane książki: {{title}}", + "Failed to purge book data: {{title}}": "Nie udało się usunąć danych książki: {{title}}", + "Purge Book Data": "Usuń wszystkie dane książki", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Spowoduje to trwałe usunięcie książki i wszystkich jej danych, w tym postępu czytania, notatek i zakładek. Tej operacji nie można cofnąć.", + "Purge Data": "Usuń wszystkie dane", + "Erase the book and all its data, including reading progress": "Usuwa książkę i wszystkie jej dane, w tym postęp czytania", + "More Actions": "Więcej akcji", + "Sign in and make the book available to share it": "Zaloguj się i pobierz książkę, aby ją udostępnić", + "Download the book to export it": "Pobierz książkę, aby ją wyeksportować" } diff --git a/apps/readest-app/public/locales/pt-BR/translation.json b/apps/readest-app/public/locales/pt-BR/translation.json index 2088b0a6..93593b20 100644 --- a/apps/readest-app/public/locales/pt-BR/translation.json +++ b/apps/readest-app/public/locales/pt-BR/translation.json @@ -1790,5 +1790,14 @@ "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" + "Auto Sync": "Sincronização automática", + "Purged book data: {{title}}": "Dados do livro apagados: {{title}}", + "Failed to purge book data: {{title}}": "Falha ao apagar os dados do livro: {{title}}", + "Purge Book Data": "Apagar todos os dados do livro", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Isso apaga permanentemente o livro e todos os seus dados, incluindo o progresso de leitura, notas e marcadores. Esta ação não pode ser desfeita.", + "Purge Data": "Apagar todos os dados", + "Erase the book and all its data, including reading progress": "Apaga o livro e todos os seus dados, incluindo o progresso de leitura", + "More Actions": "Mais ações", + "Sign in and make the book available to share it": "Faça login e tenha o livro disponível para compartilhá-lo", + "Download the book to export it": "Baixe o livro para exportá-lo" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 42c31761..61c0290a 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -1790,5 +1790,14 @@ "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" + "Auto Sync": "Sincronização Automática", + "Purged book data: {{title}}": "Dados do livro apagados: {{title}}", + "Failed to purge book data: {{title}}": "Falha ao apagar os dados do livro: {{title}}", + "Purge Book Data": "Apagar todos os dados do livro", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Isto apaga permanentemente o livro e todos os seus dados, incluindo o progresso de leitura, notas e marcadores. Esta ação não pode ser desfeita.", + "Purge Data": "Apagar todos os dados", + "Erase the book and all its data, including reading progress": "Apaga o livro e todos os seus dados, incluindo o progresso de leitura", + "More Actions": "Mais ações", + "Sign in and make the book available to share it": "Inicie sessão e tenha o livro disponível para o partilhar", + "Download the book to export it": "Transfira o livro para o exportar" } diff --git a/apps/readest-app/public/locales/ro/translation.json b/apps/readest-app/public/locales/ro/translation.json index b27afa1b..d64e23e6 100644 --- a/apps/readest-app/public/locales/ro/translation.json +++ b/apps/readest-app/public/locales/ro/translation.json @@ -1790,5 +1790,14 @@ "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ă" + "Auto Sync": "Sincronizare automată", + "Purged book data: {{title}}": "Datele cărții au fost șterse: {{title}}", + "Failed to purge book data: {{title}}": "Ștergerea datelor cărții a eșuat: {{title}}", + "Purge Book Data": "Șterge toate datele cărții", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Aceasta șterge definitiv cartea și toate datele sale, inclusiv progresul lecturii, notițele și marcajele. Această acțiune nu poate fi anulată.", + "Purge Data": "Șterge toate datele", + "Erase the book and all its data, including reading progress": "Șterge cartea și toate datele sale, inclusiv progresul lecturii", + "More Actions": "Mai multe acțiuni", + "Sign in and make the book available to share it": "Conectează-te și fă cartea disponibilă pentru a o partaja", + "Download the book to export it": "Descarcă cartea pentru a o exporta" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 3a021f36..37922dc6 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -1823,5 +1823,14 @@ "Clear Pending": "Очистить ожидающие", "Reading statistics": "Статистика чтения", "Reading time and pages read, synced across your devices and KOReader.": "Время чтения и прочитанные страницы, синхронизируются между вашими устройствами и KOReader.", - "Auto Sync": "Автосинхронизация" + "Auto Sync": "Автосинхронизация", + "Purged book data: {{title}}": "Данные книги очищены: {{title}}", + "Failed to purge book data: {{title}}": "Не удалось очистить данные книги: {{title}}", + "Purge Book Data": "Очистить данные книги", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Это навсегда удалит книгу и все её данные, включая прогресс чтения, заметки и закладки. Это действие нельзя отменить.", + "Purge Data": "Очистить данные", + "Erase the book and all its data, including reading progress": "Удаляет книгу и все её данные, включая прогресс чтения", + "More Actions": "Ещё действия", + "Sign in and make the book available to share it": "Войдите и сделайте книгу доступной, чтобы поделиться ею", + "Download the book to export it": "Скачайте книгу, чтобы экспортировать её" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 3fb57c09..9d5364c6 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "බලාපොරොත්තුවේ ඇති ඒවා මකන්න", "Reading statistics": "කියවීමේ සංඛ්‍යාලේඛන", "Reading time and pages read, synced across your devices and KOReader.": "කියවීමේ කාලය සහ කියවූ පිටු, ඔබේ උපාංග සහ KOReader හරහා සමමුහුර්ත වේ.", - "Auto Sync": "ස්වයංක්‍රීය සමමුහුර්තය" + "Auto Sync": "ස්වයංක්‍රීය සමමුහුර්තය", + "Purged book data: {{title}}": "පොතේ දත්ත මකා දමන ලදී: {{title}}", + "Failed to purge book data: {{title}}": "පොතේ දත්ත මැකීමට අසමත් විය: {{title}}", + "Purge Book Data": "පොතේ සියලු දත්ත මකන්න", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "මෙය පොත සහ කියවීමේ ප්‍රගතිය, සටහන් සහ පිටු සලකුණු ඇතුළුව එහි සියලු දත්ත ස්ථිරවම මකා දමයි. මෙය පෙරළිය නොහැක.", + "Purge Data": "සියලු දත්ත මකන්න", + "Erase the book and all its data, including reading progress": "කියවීමේ ප්‍රගතිය ඇතුළුව පොත සහ එහි සියලු දත්ත මකන්න", + "More Actions": "තවත් ක්‍රියා", + "Sign in and make the book available to share it": "බෙදා ගැනීමට පුරනය වී පොත ලබා ගත හැකි කරන්න", + "Download the book to export it": "අපනයනය කිරීමට පොත බාගන්න" } diff --git a/apps/readest-app/public/locales/sl/translation.json b/apps/readest-app/public/locales/sl/translation.json index c91f1494..f4756aa4 100644 --- a/apps/readest-app/public/locales/sl/translation.json +++ b/apps/readest-app/public/locales/sl/translation.json @@ -1823,5 +1823,14 @@ "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" + "Auto Sync": "Samodejna sinhronizacija", + "Purged book data: {{title}}": "Podatki knjige izbrisani: {{title}}", + "Failed to purge book data: {{title}}": "Podatkov knjige ni bilo mogoče izbrisati: {{title}}", + "Purge Book Data": "Izbriši vse podatke knjige", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "S tem trajno izbrišete knjigo in vse njene podatke, vključno z napredkom branja, zapiski in zaznamki. Tega dejanja ni mogoče razveljaviti.", + "Purge Data": "Izbriši vse podatke", + "Erase the book and all its data, including reading progress": "Izbriše knjigo in vse njene podatke, vključno z napredkom branja", + "More Actions": "Več dejanj", + "Sign in and make the book available to share it": "Prijavite se in omogočite knjigo za deljenje", + "Download the book to export it": "Prenesite knjigo, da jo izvozite" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index 17606940..35ea001e 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Automatisk synkronisering", + "Purged book data: {{title}}": "Bokdata rensad: {{title}}", + "Failed to purge book data: {{title}}": "Det gick inte att rensa bokdata: {{title}}", + "Purge Book Data": "Rensa all bokdata", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Detta raderar permanent boken och all dess data, inklusive läsförlopp, anteckningar och bokmärken. Detta kan inte ångras.", + "Purge Data": "Rensa all data", + "Erase the book and all its data, including reading progress": "Raderar boken och all data, inklusive läsförlopp", + "More Actions": "Fler åtgärder", + "Sign in and make the book available to share it": "Logga in och gör boken tillgänglig för att dela den", + "Download the book to export it": "Ladda ner boken för att exportera den" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index 51d23085..2c4a0293 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -1757,5 +1757,14 @@ "Clear Pending": "காத்திருப்பதை அழிக்கவும்", "Reading statistics": "வாசிப்பு புள்ளிவிவரங்கள்", "Reading time and pages read, synced across your devices and KOReader.": "வாசிப்பு நேரம் மற்றும் படித்த பக்கங்கள், உங்கள் சாதனங்கள் மற்றும் KOReader இடையே ஒத்திசைக்கப்படும்.", - "Auto Sync": "தானியங்கு ஒத்திசைவு" + "Auto Sync": "தானியங்கு ஒத்திசைவு", + "Purged book data: {{title}}": "புத்தகத் தரவு அழிக்கப்பட்டது: {{title}}", + "Failed to purge book data: {{title}}": "புத்தகத் தரவை அழிக்க முடியவில்லை: {{title}}", + "Purge Book Data": "புத்தகத்தின் எல்லா தரவையும் அழி", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "இது புத்தகத்தையும் வாசிப்பு முன்னேற்றம், குறிப்புகள் மற்றும் புத்தகக்குறிகள் உள்ளிட்ட அதன் அனைத்து தரவையும் நிரந்தரமாக அழிக்கும். இதை மீட்டெடுக்க முடியாது.", + "Purge Data": "எல்லா தரவையும் அழி", + "Erase the book and all its data, including reading progress": "வாசிப்பு முன்னேற்றம் உட்பட புத்தகத்தையும் அதன் எல்லா தரவையும் அழிக்கும்", + "More Actions": "மேலும் செயல்கள்", + "Sign in and make the book available to share it": "பகிர உள்நுழைந்து புத்தகத்தைக் கிடைக்கச் செய்யவும்", + "Download the book to export it": "ஏற்றுமதி செய்ய புத்தகத்தைப் பதிவிறக்கவும்" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index 49e0de70..d17d0ced 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "ล้างที่รอดำเนินการ", "Reading statistics": "สถิติการอ่าน", "Reading time and pages read, synced across your devices and KOReader.": "เวลาในการอ่านและจำนวนหน้าที่อ่าน ซิงค์ข้ามอุปกรณ์ของคุณและ KOReader", - "Auto Sync": "ซิงค์อัตโนมัติ" + "Auto Sync": "ซิงค์อัตโนมัติ", + "Purged book data: {{title}}": "ล้างข้อมูลหนังสือแล้ว: {{title}}", + "Failed to purge book data: {{title}}": "ล้างข้อมูลหนังสือไม่สำเร็จ: {{title}}", + "Purge Book Data": "ล้างข้อมูลหนังสือทั้งหมด", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "การดำเนินการนี้จะลบหนังสือและข้อมูลทั้งหมดอย่างถาวร รวมถึงความคืบหน้าการอ่าน บันทึก และที่คั่นหนังสือ ไม่สามารถเลิกทำได้", + "Purge Data": "ล้างข้อมูลทั้งหมด", + "Erase the book and all its data, including reading progress": "ลบหนังสือและข้อมูลทั้งหมด รวมถึงความคืบหน้าการอ่าน", + "More Actions": "การทำงานเพิ่มเติม", + "Sign in and make the book available to share it": "ลงชื่อเข้าใช้และทำให้หนังสือพร้อมใช้งานเพื่อแชร์", + "Download the book to export it": "ดาวน์โหลดหนังสือเพื่อส่งออก" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index e285f38f..e483d699 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Otomatik Senkronizasyon", + "Purged book data: {{title}}": "Kitap verileri silindi: {{title}}", + "Failed to purge book data: {{title}}": "Kitap verileri silinemedi: {{title}}", + "Purge Book Data": "Tüm kitap verilerini sil", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Bu işlem kitabı ve okuma ilerlemesi, notlar ve yer imleri dahil tüm verilerini kalıcı olarak siler. Bu işlem geri alınamaz.", + "Purge Data": "Tüm verileri sil", + "Erase the book and all its data, including reading progress": "Kitabı ve okuma ilerlemesi dahil tüm verilerini siler", + "More Actions": "Diğer eylemler", + "Sign in and make the book available to share it": "Paylaşmak için oturum açın ve kitabı kullanılabilir hale getirin", + "Download the book to export it": "Dışa aktarmak için kitabı indirin" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index c068dc66..471055b5 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -1823,5 +1823,14 @@ "Clear Pending": "Очистити очікувані", "Reading statistics": "Статистика читання", "Reading time and pages read, synced across your devices and KOReader.": "Час читання та прочитані сторінки, синхронізуються між вашими пристроями та KOReader.", - "Auto Sync": "Автоматична синхронізація" + "Auto Sync": "Автоматична синхронізація", + "Purged book data: {{title}}": "Дані книги очищено: {{title}}", + "Failed to purge book data: {{title}}": "Не вдалося очистити дані книги: {{title}}", + "Purge Book Data": "Очистити дані книги", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Це назавжди видалить книгу та всі її дані, зокрема прогрес читання, нотатки та закладки. Цю дію неможливо скасувати.", + "Purge Data": "Очистити дані", + "Erase the book and all its data, including reading progress": "Видаляє книгу та всі її дані, зокрема прогрес читання", + "More Actions": "Більше дій", + "Sign in and make the book available to share it": "Увійдіть і зробіть книгу доступною, щоб поділитися нею", + "Download the book to export it": "Завантажте книгу, щоб експортувати її" } diff --git a/apps/readest-app/public/locales/uz/translation.json b/apps/readest-app/public/locales/uz/translation.json index 6444597b..b0d31f01 100644 --- a/apps/readest-app/public/locales/uz/translation.json +++ b/apps/readest-app/public/locales/uz/translation.json @@ -1757,5 +1757,14 @@ "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" + "Auto Sync": "Avtomatik sinxronlash", + "Purged book data: {{title}}": "Kitob maʼlumotlari oʻchirildi: {{title}}", + "Failed to purge book data: {{title}}": "Kitob maʼlumotlarini oʻchirib boʻlmadi: {{title}}", + "Purge Book Data": "Kitobning barcha maʼlumotlarini oʻchirish", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Bu kitobni va uning barcha maʼlumotlarini, jumladan oʻqish jarayoni, eslatmalar va xatcho‘plarni butunlay oʻchiradi. Buni qaytarib boʻlmaydi.", + "Purge Data": "Barcha maʼlumotlarni oʻchirish", + "Erase the book and all its data, including reading progress": "Kitobni va uning barcha maʼlumotlarini, jumladan oʻqish jarayonini oʻchiradi", + "More Actions": "Qoʻshimcha amallar", + "Sign in and make the book available to share it": "Ulashish uchun tizimga kiring va kitobni mavjud qiling", + "Download the book to export it": "Eksport qilish uchun kitobni yuklab oling" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index b0cad8a5..a008075e 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -1724,5 +1724,14 @@ "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ộ" + "Auto Sync": "Tự động đồng bộ", + "Purged book data: {{title}}": "Đã xóa dữ liệu sách: {{title}}", + "Failed to purge book data: {{title}}": "Không thể xóa dữ liệu sách: {{title}}", + "Purge Book Data": "Xóa toàn bộ dữ liệu sách", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "Thao tác này sẽ xóa vĩnh viễn sách và toàn bộ dữ liệu của nó, bao gồm tiến độ đọc, ghi chú và dấu trang. Không thể hoàn tác.", + "Purge Data": "Xóa toàn bộ dữ liệu", + "Erase the book and all its data, including reading progress": "Xóa sách và toàn bộ dữ liệu, bao gồm tiến độ đọc", + "More Actions": "Thao tác khác", + "Sign in and make the book available to share it": "Đăng nhập và tải sách về để chia sẻ", + "Download the book to export it": "Tải sách về để xuất ra" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index 5b36ddab..73e93716 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "清除等待中", "Reading statistics": "阅读统计", "Reading time and pages read, synced across your devices and KOReader.": "阅读时间和已读页数,在您的设备和 KOReader 之间同步。", - "Auto Sync": "自动同步" + "Auto Sync": "自动同步", + "Purged book data: {{title}}": "已清除书籍数据:{{title}}", + "Failed to purge book data: {{title}}": "清除书籍数据失败:{{title}}", + "Purge Book Data": "清除书籍数据", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "这将永久删除该书籍及其所有数据,包括阅读进度、笔记和书签。此操作无法撤销。", + "Purge Data": "清除数据", + "Erase the book and all its data, including reading progress": "删除该书籍及其所有数据,包括阅读进度", + "More Actions": "更多操作", + "Sign in and make the book available to share it": "登录并使书籍可用后即可分享", + "Download the book to export it": "下载书籍后即可导出" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index be2be63b..d785d970 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -1724,5 +1724,14 @@ "Clear Pending": "清除等待中", "Reading statistics": "閱讀統計", "Reading time and pages read, synced across your devices and KOReader.": "閱讀時間與已讀頁數,在您的裝置與 KOReader 之間同步。", - "Auto Sync": "自動同步" + "Auto Sync": "自動同步", + "Purged book data: {{title}}": "已清除書籍資料:{{title}}", + "Failed to purge book data: {{title}}": "清除書籍資料失敗:{{title}}", + "Purge Book Data": "清除書籍資料", + "This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.": "這將永久刪除該書籍及其所有資料,包括閱讀進度、筆記和書籤。此操作無法復原。", + "Purge Data": "清除資料", + "Erase the book and all its data, including reading progress": "刪除該書籍及其所有資料,包括閱讀進度", + "More Actions": "更多操作", + "Sign in and make the book available to share it": "登入並使書籍可用後即可分享", + "Download the book to export it": "下載書籍後即可匯出" } diff --git a/apps/readest-app/src/__tests__/components/BookDetailModal.test.tsx b/apps/readest-app/src/__tests__/components/BookDetailModal.test.tsx index 82fe2cee..77f6619f 100644 --- a/apps/readest-app/src/__tests__/components/BookDetailModal.test.tsx +++ b/apps/readest-app/src/__tests__/components/BookDetailModal.test.tsx @@ -17,6 +17,10 @@ vi.mock('@/context/EnvContext', () => ({ useEnv: () => ({ envConfig: { getAppService: async () => appSvc }, appService: appSvc }), })); +vi.mock('@/context/AuthContext', () => ({ + useAuth: () => ({ user: { id: 'u1' } }), +})); + vi.mock('@/store/themeStore', () => ({ useThemeStore: () => ({ safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 0 } }), })); diff --git a/apps/readest-app/src/__tests__/components/BookDetailView.test.tsx b/apps/readest-app/src/__tests__/components/BookDetailView.test.tsx index cedce91e..dec935ad 100644 --- a/apps/readest-app/src/__tests__/components/BookDetailView.test.tsx +++ b/apps/readest-app/src/__tests__/components/BookDetailView.test.tsx @@ -29,6 +29,10 @@ vi.mock('@/helpers/settings', () => ({ saveSysSettings: vi.fn(), })); +vi.mock('@/utils/open', () => ({ + openExternalUrl: vi.fn(), +})); + vi.mock('@/components/BookCover', () => ({ __esModule: true, default: () => null, @@ -103,6 +107,85 @@ describe('BookDetailView delete dropdown layout', () => { }); }); +describe('BookDetailView More menu (Goodreads + Share)', () => { + const openMore = (container: HTMLElement) => { + const toggle = container.querySelector('button[aria-label="More Actions"]'); + expect(toggle).toBeTruthy(); + fireEvent.click(toggle!); + }; + + it('folds Goodreads and Share into the hamburger menu', () => { + const { container, getByText } = renderView({ onShare: vi.fn(), shareEnabled: true }); + // Goodreads is no longer a standalone icon button outside the menu. + expect(container.querySelector('button[aria-label="More Actions"]')).toBeTruthy(); + openMore(container); + expect(getByText('Search on Goodreads')).toBeTruthy(); + expect(getByText('Share Book')).toBeTruthy(); + }); + + it('enables Share and calls onShare when the book is shareable', () => { + const onShare = vi.fn(); + const { container, getByText } = renderView({ onShare, shareEnabled: true }); + openMore(container); + const shareButton = getByText('Share Book').closest('button'); + expect(shareButton).toBeTruthy(); + expect(shareButton!.disabled).toBe(false); + fireEvent.click(shareButton!); + expect(onShare).toHaveBeenCalledTimes(1); + }); + + it('disables Share when not shareable (logged out or no local file)', () => { + const onShare = vi.fn(); + const { container, getByText } = renderView({ onShare, shareEnabled: false }); + openMore(container); + const shareButton = getByText('Share Book').closest('button'); + expect(shareButton!.disabled).toBe(true); + fireEvent.click(shareButton!); + expect(onShare).not.toHaveBeenCalled(); + }); + + it('keeps Export in the More menu and calls onExport when the file exists', () => { + const onExport = vi.fn(); + const { container, getByText } = renderView({ onExport, fileSize: 1024 }); + openMore(container); + const exportButton = getByText('Export Book').closest('button'); + expect(exportButton).toBeTruthy(); + expect(exportButton!.disabled).toBe(false); + fireEvent.click(exportButton!); + expect(onExport).toHaveBeenCalledTimes(1); + }); + + it('disables Export when the book has no local file', () => { + const onExport = vi.fn(); + const { container, getByText } = renderView({ onExport, fileSize: null }); + openMore(container); + const exportButton = getByText('Export Book').closest('button'); + expect(exportButton!.disabled).toBe(true); + fireEvent.click(exportButton!); + expect(onExport).not.toHaveBeenCalled(); + }); +}); + +describe('BookDetailView Purge Data action', () => { + it('offers Purge Data in the delete dropdown and calls onPurge', () => { + const onPurge = vi.fn(); + const { container, getByText } = renderView({ onPurge }); + const toggle = container.querySelector('button[aria-label="Delete Book Options"]'); + fireEvent.click(toggle!); + const purgeButton = getByText('Purge Data').closest('button'); + expect(purgeButton).toBeTruthy(); + fireEvent.click(purgeButton!); + expect(onPurge).toHaveBeenCalledTimes(1); + }); + + it('omits Purge Data when onPurge is not provided', () => { + const { container, queryByText } = renderView(); + const toggle = container.querySelector('button[aria-label="Delete Book Options"]'); + fireEvent.click(toggle!); + expect(queryByText('Purge Data')).toBeNull(); + }); +}); + describe('BookDetailView file path row', () => { // book.filePath is only set for in-place imports (and OS-handed paths like // Android "Open with Readest"). Hash-copy imports leave it undefined, so diff --git a/apps/readest-app/src/__tests__/services/cloud-service.test.ts b/apps/readest-app/src/__tests__/services/cloud-service.test.ts index 845ed921..10909f7b 100644 --- a/apps/readest-app/src/__tests__/services/cloud-service.test.ts +++ b/apps/readest-app/src/__tests__/services/cloud-service.test.ts @@ -176,6 +176,73 @@ describe('cloudService', () => { }); }); + // Purge is the "Cloud & Device" delete PLUS a full wipe of the + // app-generated Books// directory (config.json reading progress/notes, + // nav.json, cover.png) that the other deletes leave behind (issue #4615). + // The local-file side lives here; the tombstone + queued cloud deletion are + // owned by the page's handleBookDelete, exactly like the 'both'/'local' + // split, so this branch must NOT touch deletedAt or the cloud. + describe('purge delete action', () => { + test('removes the entire Books// directory', async () => { + const book = createMockBook(); + await deleteBook(mockFs, book, 'purge'); + + expect(mockFs.removeDir).toHaveBeenCalledWith(book.hash, 'Books', true); + }); + + test('does not remove the managed book file individually (the dir wipe covers it)', async () => { + const book = createMockBook(); + await deleteBook(mockFs, book, 'purge'); + + // The whole directory is removed in one shot; no per-file removeFile. + expect(mockFs.removeFile).not.toHaveBeenCalled(); + }); + + test('clears downloadedAt but leaves deletedAt for the caller (mirrors local)', async () => { + const book = createMockBook({ downloadedAt: 12345, deletedAt: null }); + await deleteBook(mockFs, book, 'purge'); + + expect(book.downloadedAt).toBeNull(); + expect(book.deletedAt).toBeNull(); + }); + + test('does not delete cloud files (the page queues the cloud deletion)', async () => { + const { deleteFile: deleteCloudFile } = await import('@/libs/storage'); + const book = createMockBook({ uploadedAt: 1000 }); + await deleteBook(mockFs, book, 'purge'); + + expect(deleteCloudFile).not.toHaveBeenCalled(); + // uploadedAt is left intact for the queued cloud-delete transfer to clear. + expect(book.uploadedAt).toBe(1000); + }); + + test('removes the in-place source file and the sidecar dir for in-place books', async () => { + const book = createMockBook({ filePath: '/Users/me/Library/sample.epub' }); + vi.mocked(mockFs.exists).mockImplementation(async (path, base) => { + if (base === 'None' && path === book.filePath) return true; + if (base === 'Books' && path === book.hash) return true; + return false; + }); + + await deleteBook(mockFs, book, 'purge'); + + // External source file (outside Books//) is removed... + expect(mockFs.removeFile).toHaveBeenCalledWith('/Users/me/Library/sample.epub', 'None'); + // ...and the metadata sidecar directory is wiped too. + expect(mockFs.removeDir).toHaveBeenCalledWith(book.hash, 'Books', true); + }); + + test('does not throw when the directory does not exist', async () => { + vi.mocked(mockFs.exists).mockResolvedValue(false); + const book = createMockBook({ downloadedAt: 12345 }); + + await deleteBook(mockFs, book, 'purge'); + + expect(mockFs.removeDir).not.toHaveBeenCalled(); + expect(book.downloadedAt).toBeNull(); + }); + }); + describe('cloud delete action', () => { test('does not delete local files', async () => { const book = createMockBook({ uploadedAt: 1000 }); diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index fd5526c0..c467dbc6 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -877,18 +877,22 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP both: _('Book deleted: {{title}}', { title: book.title }), cloud: _('Deleted cloud backup of the book: {{title}}', { title: book.title }), local: _('Deleted local copy of the book: {{title}}', { title: book.title }), + purge: _('Purged book data: {{title}}', { title: book.title }), }; const deletionFailMessages = { both: _('Failed to delete book: {{title}}', { title: book.title }), cloud: _('Failed to delete cloud backup of the book: {{title}}', { title: book.title }), local: _('Failed to delete local copy of the book: {{title}}', { title: book.title }), + purge: _('Failed to purge book data: {{title}}', { title: book.title }), }; try { - // Handle local deletion immediately - if (deleteAction === 'local' || deleteAction === 'both') { - await appService?.deleteBook(book, 'local'); - if (deleteAction === 'both') { + // Handle local deletion immediately. Purge mirrors 'both' (tombstone + + // queued cloud delete) but hands 'purge' to deleteBook, which also wipes + // the entire Books// folder (config/nav/cover) — issue #4615. + if (deleteAction === 'local' || deleteAction === 'both' || deleteAction === 'purge') { + await appService?.deleteBook(book, deleteAction === 'purge' ? 'purge' : 'local'); + if (deleteAction === 'both' || deleteAction === 'purge') { book.deletedAt = Date.now(); book.downloadedAt = null; book.coverDownloadedAt = null; @@ -899,7 +903,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP } // Queue cloud deletion - if (deleteAction === 'cloud' || deleteAction === 'both') { + if (deleteAction === 'cloud' || deleteAction === 'both' || deleteAction === 'purge') { const transferId = transferManager.queueDelete(book, 1, true); if (!transferId) { throw new Error('Failed to queue cloud deletion'); @@ -1468,6 +1472,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP handleBookDelete={handleBookDelete('both')} handleBookDeleteCloudBackup={handleBookDelete('cloud')} handleBookDeleteLocalCopy={handleBookDelete('local')} + handleBookPurge={handleBookDelete('purge')} handleBookMetadataUpdate={handleUpdateMetadata} /> )} diff --git a/apps/readest-app/src/components/metadata/BookDetailModal.tsx b/apps/readest-app/src/components/metadata/BookDetailModal.tsx index 6e60751e..d117bde4 100644 --- a/apps/readest-app/src/components/metadata/BookDetailModal.tsx +++ b/apps/readest-app/src/components/metadata/BookDetailModal.tsx @@ -5,6 +5,7 @@ import { Book } from '@/types/book'; import { getBookWithUpdatedMetadata } from '@/utils/book'; import { BookMetadata } from '@/libs/document'; import { useEnv } from '@/context/EnvContext'; +import { useAuth } from '@/context/AuthContext'; import { useThemeStore } from '@/store/themeStore'; import { useTranslation } from '@/hooks/useTranslation'; import { useMetadataEdit } from './useMetadataEdit'; @@ -27,6 +28,7 @@ interface BookDetailModalProps { handleBookDelete?: (book: Book) => void; handleBookDeleteCloudBackup?: (book: Book) => void; handleBookDeleteLocalCopy?: (book: Book) => void; + handleBookPurge?: (book: Book) => void; handleBookMetadataUpdate?: (book: Book, updatedMetadata: BookMetadata) => void; } @@ -45,10 +47,12 @@ const BookDetailModal: React.FC = ({ handleBookDelete, handleBookDeleteCloudBackup, handleBookDeleteLocalCopy, + handleBookPurge, handleBookMetadataUpdate, }) => { const _ = useTranslation(); const { envConfig, appService } = useEnv(); + const { user } = useAuth(); const { safeAreaInsets } = useThemeStore(); const [activeDeleteAction, setActiveDeleteAction] = useState(null); const [isLoading, setIsLoading] = useState(false); @@ -95,6 +99,13 @@ const BookDetailModal: React.FC = ({ message: _('Are you sure to delete the local copy of the selected book?'), handler: handleBookDeleteLocalCopy, }, + purge: { + title: _('Purge Book Data'), + message: _( + 'This permanently erases the book and all its data, including reading progress, notes, and bookmarks. This cannot be undone.', + ), + handler: handleBookPurge, + }, }; useEffect(() => { @@ -168,19 +179,14 @@ const BookDetailModal: React.FC = ({ const handleDelete = () => handleDeleteAction('both'); const handleDeleteCloudBackup = () => handleDeleteAction('cloud'); const handleDeleteLocalCopy = () => handleDeleteAction('local'); + const handlePurge = () => handleDeleteAction('purge'); - const handleRedownload = async () => { + const handleShare = () => { + // Close this modal first, then hand off to the share dialog hosted by + // Bookshelf (it owns the login gate + ShareBookDialog). Mirrors how the + // bookshelf context menu dispatches the same event. handleClose(); - if (handleBookDownload) { - handleBookDownload(book, { redownload: true, queued: false }); - } - }; - - const handleReupload = async () => { - handleClose(); - if (handleBookUpload) { - handleBookUpload(book); - } + eventDispatcher.dispatch('show-share-dialog', { book }); }; const handleBookExport = async () => { @@ -197,6 +203,25 @@ const BookDetailModal: React.FC = ({ }, 0); }; + const handleRedownload = async () => { + handleClose(); + if (handleBookDownload) { + handleBookDownload(book, { redownload: true, queued: false }); + } + }; + + const handleReupload = async () => { + handleClose(); + if (handleBookUpload) { + handleBookUpload(book); + } + }; + + // Sharing uploads the book to the Readest backend and mints a public link, so + // it needs a signed-in user and a resolvable on-disk file. `fileSize` is only + // non-null when getBookFileSize could actually open the local file. + const shareEnabled = !!user && fileSize !== null; + const currentDeleteConfig = activeDeleteAction ? deleteConfigs[activeDeleteAction] : null; return ( @@ -235,14 +260,17 @@ const BookDetailModal: React.FC = ({ book={displayBook} metadata={bookMeta} fileSize={fileSize} + shareEnabled={shareEnabled} onEdit={handleBookMetadataUpdate ? handleEditMetadata : undefined} onDelete={handleBookDelete ? handleDelete : undefined} onDeleteCloudBackup={ handleBookDeleteCloudBackup ? handleDeleteCloudBackup : undefined } onDeleteLocalCopy={handleBookDeleteLocalCopy ? handleDeleteLocalCopy : undefined} + onPurge={handleBookPurge ? handlePurge : undefined} onDownload={handleBookDownload ? handleRedownload : undefined} onUpload={handleBookUpload ? handleReupload : undefined} + onShare={handleShare} onExport={handleBookExport} /> )} diff --git a/apps/readest-app/src/components/metadata/BookDetailView.tsx b/apps/readest-app/src/components/metadata/BookDetailView.tsx index 07c166f6..96bdff15 100644 --- a/apps/readest-app/src/components/metadata/BookDetailView.tsx +++ b/apps/readest-app/src/components/metadata/BookDetailView.tsx @@ -5,11 +5,10 @@ import { MdOutlineCloudUpload, MdOutlineDelete, MdOutlineEdit, - MdSaveAlt, + MdMenu, MdExpandMore, MdExpandLess, } from 'react-icons/md'; -import { FaGoodreads } from 'react-icons/fa'; import { Book } from '@/types/book'; import { BookMetadata } from '@/libs/document'; @@ -35,12 +34,15 @@ interface BookDetailViewProps { book: Book; metadata: BookMetadata | null; fileSize: number | null; + shareEnabled?: boolean; onEdit?: () => void; onDelete?: () => void; onDeleteCloudBackup?: () => void; onDeleteLocalCopy?: () => void; + onPurge?: () => void; onDownload?: () => void; onUpload?: () => void; + onShare?: () => void; onExport?: () => void; } @@ -48,18 +50,25 @@ const BookDetailView: React.FC = ({ book, metadata, fileSize, + shareEnabled, onEdit, onDelete, onDeleteCloudBackup, onDeleteLocalCopy, + onPurge, onDownload, onUpload, + onShare, onExport, }) => { const _ = useTranslation(); const { envConfig } = useEnv(); const { settings } = useSettingsStore(); + // Export and Share both read the book file off disk; `fileSize` is only + // non-null when getBookFileSize could actually open the local copy. + const hasLocalFile = fileSize !== null; + const toggleSeriesCollapse = () => { saveSysSettings(envConfig, 'metadataSeriesCollapsed', !settings.metadataSeriesCollapsed); }; @@ -101,12 +110,16 @@ const BookDetailView: React.FC = ({ )} - + {book.uploadedAt && onDownload && ( + + )} + {book.downloadedAt && onUpload && ( + + )} {onDelete && ( = ({ onClick={onDeleteLocalCopy} disabled={!book.downloadedAt} /> + {onPurge && ( + + )} )} - {book.uploadedAt && onDownload && ( - - )} - {book.downloadedAt && onUpload && ( - - )} - {book.downloadedAt && onExport && ( - - )} + } + > +
+ + openExternalUrl(getGoodreadsSearchUrl(getBookGoodreadsQuery(book))) + } + /> + {onShare && ( + + )} + {onExport && ( + + )} +
+
diff --git a/apps/readest-app/src/services/cloudService.ts b/apps/readest-app/src/services/cloudService.ts index cb7d363e..332c2639 100644 --- a/apps/readest-app/src/services/cloudService.ts +++ b/apps/readest-app/src/services/cloudService.ts @@ -24,7 +24,7 @@ export async function deleteBook( book: Book, deleteAction: DeleteAction, ): Promise { - if (deleteAction === 'local' || deleteAction === 'both') { + if (deleteAction === 'local' || deleteAction === 'both' || deleteAction === 'purge') { const source = await resolveBookContentSource(fs, book); if (source.kind === 'external') { try { @@ -36,16 +36,32 @@ export async function deleteBook( // the metadata-side bookkeeping that follows. console.log('Failed to remove in-place source file:', error); } - } else if (source.kind === 'managed') { + } else if (source.kind === 'managed' && deleteAction !== 'purge') { + // Purge wipes the whole directory below, so skip the per-file removal. if (await fs.exists(source.path, source.base)) { await fs.removeFile(source.path, source.base); } } + // Purge erases the entire app-generated Books// directory — the + // managed book file, cover.png, and (the reason for issue #4615) + // config.json (reading progress, notes, bookmarks) + nav.json that the + // other delete actions leave behind. For in-place books the external + // source file was already removed above; this clears the sidecar dir. + if (deleteAction === 'purge') { + const dir = getDir(book); + if (await fs.exists(dir, 'Books')) { + await fs.removeDir(dir, 'Books', true); + } + } + if (deleteAction === 'both' && (await fs.exists(getCoverFilename(book), 'Books'))) { await fs.removeFile(getCoverFilename(book), 'Books'); } - if (deleteAction === 'local') { + if (deleteAction === 'local' || deleteAction === 'purge') { + // Mirror 'local': mark not-downloaded but leave the tombstone (deletedAt) + // to the caller. The page's handleBookDelete sets deletedAt and queues the + // cloud deletion for purge, exactly as it does for the 'both' action. book.downloadedAt = null; } else { book.deletedAt = Date.now(); diff --git a/apps/readest-app/src/types/system.ts b/apps/readest-app/src/types/system.ts index f4dbd434..1863dc6a 100644 --- a/apps/readest-app/src/types/system.ts +++ b/apps/readest-app/src/types/system.ts @@ -15,7 +15,7 @@ export type AppPlatform = 'web' | 'tauri' | 'node'; export type OsPlatform = 'android' | 'ios' | 'macos' | 'windows' | 'linux' | 'unknown'; // biome-ignore format: keep the union members compact on a single line export type BaseDir = | 'Books' | 'Settings' | 'Data' | 'Fonts' | 'Images' | 'Dictionaries' | 'Log' | 'Cache' | 'Temp' | 'None'; -export type DeleteAction = 'cloud' | 'local' | 'both'; +export type DeleteAction = 'cloud' | 'local' | 'both' | 'purge'; export type SelectDirectoryMode = 'read' | 'write'; export type DistChannel = 'readest' | 'playstore' | 'appstore' | 'unknown';