From 8f22a5c570ec1073264aed85d3deba1aea4bdb76 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Mon, 24 Nov 2025 00:00:49 +0800 Subject: [PATCH] feat: select directory to save last book cover image (#2521) --- .../public/locales/ar/translation.json | 3 +- .../public/locales/bn/translation.json | 3 +- .../public/locales/bo/translation.json | 3 +- .../public/locales/de/translation.json | 3 +- .../public/locales/el/translation.json | 3 +- .../public/locales/es/translation.json | 11 ++- .../public/locales/fa/translation.json | 3 +- .../public/locales/fr/translation.json | 3 +- .../public/locales/hi/translation.json | 3 +- .../public/locales/id/translation.json | 3 +- .../public/locales/it/translation.json | 3 +- .../public/locales/ja/translation.json | 3 +- .../public/locales/ko/translation.json | 3 +- .../public/locales/nl/translation.json | 3 +- .../public/locales/pl/translation.json | 3 +- .../public/locales/pt/translation.json | 3 +- .../public/locales/ru/translation.json | 3 +- .../public/locales/si/translation.json | 3 +- .../public/locales/sv/translation.json | 3 +- .../public/locales/ta/translation.json | 3 +- .../public/locales/th/translation.json | 3 +- .../public/locales/tr/translation.json | 3 +- .../public/locales/uk/translation.json | 3 +- .../public/locales/vi/translation.json | 3 +- .../public/locales/zh-CN/translation.json | 3 +- .../public/locales/zh-TW/translation.json | 3 +- .../src-tauri/capabilities/default.json | 3 + .../com/bilingify/readest/MainActivity.kt | 8 ++ .../src/main/java/NativeBridgePlugin.kt | 93 ++++++++++++++++++- .../tauri-plugin-native-bridge/build.rs | 1 + .../commands/select_directory.toml | 13 +++ .../permissions/autogenerated/reference.md | 27 ++++++ .../permissions/default.toml | 1 + .../permissions/schemas/schema.json | 16 +++- .../src/commands.rs | 7 ++ .../tauri-plugin-native-bridge/src/desktop.rs | 4 + .../tauri-plugin-native-bridge/src/lib.rs | 1 + .../tauri-plugin-native-bridge/src/mobile.rs | 8 ++ .../tauri-plugin-native-bridge/src/models.rs | 9 ++ .../app/library/components/SettingsMenu.tsx | 24 ++++- .../app/reader/hooks/useAutoSaveBookCover.ts | 23 ++++- .../src/services/nativeAppService.ts | 8 +- apps/readest-app/src/types/settings.ts | 1 + apps/readest-app/src/utils/bridge.ts | 12 +++ 44 files changed, 306 insertions(+), 39 deletions(-) create mode 100644 apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/commands/select_directory.toml diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index a96dd2ba..4507e9c6 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -686,5 +686,6 @@ "Traditional (Hong Kong) to Simplified": "تقليدي (هونغ كونغ) → مبسّط", "Traditional (Taiwan) to Simplified, with phrases": "تقليدي (تايوان • عبارات) → مبسّط", "Convert Simplified and Traditional Chinese": "تحويل المبسّط/التقليدي", - "Convert Mode": "وضع التحويل" + "Convert Mode": "وضع التحويل", + "Failed to auto-save book cover for lock screen: {{error}}": "فشل في الحفظ التلقائي لغلاف الكتاب لشاشة القفل: {{error}}" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index 83d79c79..f0b0f019 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "প্রথাগত (হংকং) → সরল", "Traditional (Taiwan) to Simplified, with phrases": "প্রথাগত (তাইওয়ান • বাক্য) → সরল", "Convert Simplified and Traditional Chinese": "সরল/প্রথাগত রূপান্তর", - "Convert Mode": "রূপান্তর মোড" + "Convert Mode": "রূপান্তর মোড", + "Failed to auto-save book cover for lock screen: {{error}}": "লক স্ক্রিনের জন্য বইয়ের মলাট স্বয়ংক্রিয়ভাবে সংরক্ষণ করতে ব্যর্থ: {{error}}" } diff --git a/apps/readest-app/public/locales/bo/translation.json b/apps/readest-app/public/locales/bo/translation.json index 776975fa..1e202c27 100644 --- a/apps/readest-app/public/locales/bo/translation.json +++ b/apps/readest-app/public/locales/bo/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "སྲོལ་རྒྱུན (ཧོང་ཀོང) → སྟོང་བཟོས", "Traditional (Taiwan) to Simplified, with phrases": "སྲོལ་རྒྱུན (ཏའི་ཝན • ཚིག་ཕྲེང) → སྟོང་བཟོས", "Convert Simplified and Traditional Chinese": "རྒྱ་ཡིག སྟོང་བཟོས/སྲོལ་རྒྱུན བསྒྱུར་བ", - "Convert Mode": "བསྒྱུར་བའི་རྣམ་པ" + "Convert Mode": "བསྒྱུར་བའི་རྣམ་པ", + "Failed to auto-save book cover for lock screen: {{error}}": "སྒྲོམ་སྒོར་དེབ་གནས་བཅས་པའི་སྤྱོད་བྱས་མ་ཐུབ།: {{error}}" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index 0323a53a..fa3bfe15 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "Traditionell (Hongkong) → Vereinfacht", "Traditional (Taiwan) to Simplified, with phrases": "Traditionell (Taiwan • Phrasen) → Vereinfacht", "Convert Simplified and Traditional Chinese": "Vereinfach./Traditionell konvertieren", - "Convert Mode": "Konvertierungsmodus" + "Convert Mode": "Konvertierungsmodus", + "Failed to auto-save book cover for lock screen: {{error}}": "Automatisches Speichern des Buchcovers für den Sperrbildschirm fehlgeschlagen: {{error}}" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index f4bdc708..026f2302 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "Παραδ. (Χονγκ Κονγκ) → Απλοπ.", "Traditional (Taiwan) to Simplified, with phrases": "Παραδ. (Ταϊβάν • φράσεις) → Απλοπ.", "Convert Simplified and Traditional Chinese": "Μετατροπή Απλοπ./Παραδ. Κινέζικων", - "Convert Mode": "Λειτουργία μετατροπής" + "Convert Mode": "Λειτουργία μετατροπής", + "Failed to auto-save book cover for lock screen: {{error}}": "Αποτυχία αυτόματης αποθήκευσης εξωφύλλου βιβλίου για την οθόνη κλειδώματος: {{error}}" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 7d448fea..cb7be7ed 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -667,12 +667,13 @@ "No Conversion": "Sin conversión", "Simplified to Traditional": "Simpl. → Trad.", "Traditional to Simplified": "Trad. → Simpl.", - "Simplified to Traditional (Taiwán)": "Simpl. → Trad. (Taiwán)", + "Simplified to Traditional (Taiwan)": "Simpl. → Trad. (Taiwan)", "Simplified to Traditional (Hong Kong)": "Simpl. → Trad. (Hong Kong)", - "Simplified to Traditional (Taiwán), with phrases": "Simpl. → Trad. (Taiwán • frases)", - "Traditional (Taiwán) to Simplified": "Trad. (Taiwán) → Simpl.", + "Simplified to Traditional (Taiwan), with phrases": "Simpl. → Trad. (Taiwan • frases)", + "Traditional (Taiwan) to Simplified": "Trad. (Taiwan) → Simpl.", "Traditional (Hong Kong) to Simplified": "Trad. (Hong Kong) → Simpl.", - "Traditional (Taiwán) to Simplified, with phrases": "Trad. (Taiwán • frases) → Simpl.", + "Traditional (Taiwan) to Simplified, with phrases": "Trad. (Taiwan • frases) → Simpl.", "Convert Simplified and Traditional Chinese": "Convertir chino Simpl./Trad.", - "Convert Mode": "Modo de conversión" + "Convert Mode": "Modo de conversión", + "Failed to auto-save book cover for lock screen: {{error}}": "Error al guardar automáticamente la portada del libro para la pantalla de bloqueo: {{error}}" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index 7416b48c..1f150016 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "سنتی (هنگ‌کنگ) → ساده", "Traditional (Taiwan) to Simplified, with phrases": "سنتی (تایوان • عبارات) → ساده", "Convert Simplified and Traditional Chinese": "تبدیل چینی ساده/سنتی", - "Convert Mode": "حالت تبدیل" + "Convert Mode": "حالت تبدیل", + "Failed to auto-save book cover for lock screen: {{error}}": "ذخیره‌ی خودکار جلد کتاب برای صفحه‌ی قفل ناموفق بود: {{error}}" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 8de55b19..ebf4b013 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -674,5 +674,6 @@ "Traditional (Hong Kong) to Simplified": "Trad. (Hong Kong) → Simp.", "Traditional (Taiwan) to Simplified, with phrases": "Trad. (Taïwan • phrases) → Simp.", "Convert Simplified and Traditional Chinese": "Convertir chinois simp./trad.", - "Convert Mode": "Mode de conversion" + "Convert Mode": "Mode de conversion", + "Failed to auto-save book cover for lock screen: {{error}}": "Échec de l'enregistrement automatique de la couverture du livre pour l'écran de verrouillage : {{error}}" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index f397880a..9e9b23e2 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "पारंपरिक (हांगकांग) → सरलीकृत", "Traditional (Taiwan) to Simplified, with phrases": "पारंपरिक (ताइवान • वाक्यांश) → सरलीकृत", "Convert Simplified and Traditional Chinese": "सरलीकृत/पारंपरिक चीनी रूपांतरण", - "Convert Mode": "रूपांतरण मोड" + "Convert Mode": "रूपांतरण मोड", + "Failed to auto-save book cover for lock screen: {{error}}": "लॉक स्क्रीन के लिए पुस्तक कवर स्वचालित रूप से सहेजने में विफल: {{error}}" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 9351edcd..b9921b10 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "Tradisional (Hong Kong) → Sederhana", "Traditional (Taiwan) to Simplified, with phrases": "Tradisional (Taiwan • frasa) → Sederhana", "Convert Simplified and Traditional Chinese": "Konversi Tionghoa Sederhana/Tradisional", - "Convert Mode": "Mode Konversi" + "Convert Mode": "Mode Konversi", + "Failed to auto-save book cover for lock screen: {{error}}": "Gagal menyimpan otomatis sampul buku untuk layar kunci: {{error}}" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index 6d346396..9c794c9f 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -674,5 +674,6 @@ "Traditional (Hong Kong) to Simplified": "Tradizionale (Hong Kong) → Semplificato", "Traditional (Taiwan) to Simplified, with phrases": "Tradizionale (Taiwan • frasi) → Semplificato", "Convert Simplified and Traditional Chinese": "Converti Cinese Semplificato/Tradizionale", - "Convert Mode": "Modalità conversione" + "Convert Mode": "Modalità conversione", + "Failed to auto-save book cover for lock screen: {{error}}": "Impossibile salvare automaticamente la copertina del libro per la schermata di blocco: {{error}}" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 00d31a36..24b6ca36 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "繁体字(香港) → 簡体字", "Traditional (Taiwan) to Simplified, with phrases": "繁体字(台湾) → 簡体字 • フレーズ", "Convert Simplified and Traditional Chinese": "簡体字/繁体字変換", - "Convert Mode": "変換モード" + "Convert Mode": "変換モード", + "Failed to auto-save book cover for lock screen: {{error}}": "ロック画面の本のカバーの自動保存に失敗しました:{{error}}" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index dd45e60e..b781a80f 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "번체 (홍콩) → 간체", "Traditional (Taiwan) to Simplified, with phrases": "번체 (대만) → 간체 • 문구", "Convert Simplified and Traditional Chinese": "간체/번체 변환", - "Convert Mode": "변환 모드" + "Convert Mode": "변환 모드", + "Failed to auto-save book cover for lock screen: {{error}}": "잠금 화면용 책 표지 자동 저장 실패: {{error}}" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index 159e3cb3..6dab41b6 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "Traditioneel (Hong Kong) → Vereenvoudigd", "Traditional (Taiwan) to Simplified, with phrases": "Traditioneel (Taiwan • zinnen) → Vereenvoudigd", "Convert Simplified and Traditional Chinese": "Converteer Vereenvoudigd/Traditioneel Chinees", - "Convert Mode": "Conversiemodus" + "Convert Mode": "Conversiemodus", + "Failed to auto-save book cover for lock screen: {{error}}": "Automatisch opslaan van boekomslag voor vergrendelscherm mislukt: {{error}}" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 4c9fafe4..7cab4c68 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -678,5 +678,6 @@ "Traditional (Hong Kong) to Simplified": "Tradycyjny (Hongkong) → Uproszczony", "Traditional (Taiwan) to Simplified, with phrases": "Tradycyjny (Tajwan • frazy) → Uproszczony", "Convert Simplified and Traditional Chinese": "Konwertuj chiński uproszczony/tradycyjny", - "Convert Mode": "Tryb konwersji" + "Convert Mode": "Tryb konwersji", + "Failed to auto-save book cover for lock screen: {{error}}": "Nie udało się automatycznie zapisać okładki książki dla ekranu blokady: {{error}}" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index d0f08949..fcd27079 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -674,5 +674,6 @@ "Traditional (Hong Kong) to Simplified": "Tradicional (Hong Kong) → Simplificado", "Traditional (Taiwan) to Simplified, with phrases": "Tradicional (Taiwan • frases) → Simplificado", "Convert Simplified and Traditional Chinese": "Converter Chinês Simpl./Trad.", - "Convert Mode": "Modo de conversão" + "Convert Mode": "Modo de conversão", + "Failed to auto-save book cover for lock screen: {{error}}": "Falha ao salvar automaticamente a capa do livro para a tela de bloqueio: {{error}}" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 90ab286c..1b768795 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -678,5 +678,6 @@ "Traditional (Hong Kong) to Simplified": "Традиционный (Гонконг) → Упрощённый", "Traditional (Taiwan) to Simplified, with phrases": "Традиционный (Тайвань • фразы) → Упрощённый", "Convert Simplified and Traditional Chinese": "Преобразовать упрощённый/традиционный китайский", - "Convert Mode": "Режим преобразования" + "Convert Mode": "Режим преобразования", + "Failed to auto-save book cover for lock screen: {{error}}": "Не удалось автоматически сохранить обложку книги для экрана блокировки: {{error}}" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 1821fc30..39256f9b 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "සාම්ප්‍රදායික (හොං කොං) → සරල", "Traditional (Taiwan) to Simplified, with phrases": "සාම්ප්‍රදායික (තායිවාන • වාක්‍ය) → සරල", "Convert Simplified and Traditional Chinese": "සරල/සාම්ප්‍රදායික චීන පරිවර්තනය", - "Convert Mode": "පරිවර්තන මෝඩියුලය" + "Convert Mode": "පරිවර්තන මෝඩියුලය", + "Failed to auto-save book cover for lock screen: {{error}}": "අගුළු තිරය සඳහා පොත් ආවරණය ස්වයංක්‍රීයව සුරකිීමට අසමත් විය: {{error}}" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index f46f2ea1..d39c8606 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "Traditionell (Hongkong) → Förenklad", "Traditional (Taiwan) to Simplified, with phrases": "Traditionell (Taiwan • fraser) → Förenklad", "Convert Simplified and Traditional Chinese": "Konvertera förenklad/traditionell kinesiska", - "Convert Mode": "Konverteringsläge" + "Convert Mode": "Konverteringsläge", + "Failed to auto-save book cover for lock screen: {{error}}": "Kunde inte autospara bokomslag för låsskärm: {{error}}" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index afe8fe68..90a81f25 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "பாரம்பரிய (ஹாங்காங்) → எளிய", "Traditional (Taiwan) to Simplified, with phrases": "பாரம்பரிய (தைவான் • வசனங்கள்) → எளிய", "Convert Simplified and Traditional Chinese": "எளிய/பாரம்பரிய சீன மொழி மாற்றம்", - "Convert Mode": "மாற்ற முறை" + "Convert Mode": "மாற்ற முறை", + "Failed to auto-save book cover for lock screen: {{error}}": "பூட்டு திரைக்கான புத்தகக் கோப்பை தானாகச் சேமிக்க முடியவில்லை: {{error}}" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index a338b34e..25c97138 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "ตัวเต็ม (ฮ่องกง) → ตัวย่อ", "Traditional (Taiwan) to Simplified, with phrases": "ตัวเต็ม (ไต้หวัน • วลี) → ตัวย่อ", "Convert Simplified and Traditional Chinese": "แปลงจีนตัวย่อ/ตัวเต็ม", - "Convert Mode": "โหมดแปลงค่า" + "Convert Mode": "โหมดแปลงค่า", + "Failed to auto-save book cover for lock screen: {{error}}": "ไม่สามารถบันทึกปกหนังสือสำหรับหน้าจอล็อกโดยอัตโนมัติ: {{error}}" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index 0c5ddf5a..a1ca7b37 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -670,5 +670,6 @@ "Traditional (Hong Kong) to Simplified": "Geleneksel (Hong Kong) → Basitleştirilmiş", "Traditional (Taiwan) to Simplified, with phrases": "Geleneksel (Tayvan • ifadeler) → Basitleştirilmiş", "Convert Simplified and Traditional Chinese": "Basitleştirilmiş/Geleneksel Çince Dönüştür", - "Convert Mode": "Dönüştürme Modu" + "Convert Mode": "Dönüştürme Modu", + "Failed to auto-save book cover for lock screen: {{error}}": "Kitap kapağı kilit ekranı için otomatik kaydedilemedi: {{error}}" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index 9bfaf52c..97428073 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -678,5 +678,6 @@ "Traditional (Hong Kong) to Simplified": "Традиційне (Гонконг) → Спрощене", "Traditional (Taiwan) to Simplified, with phrases": "Традиційне (Тайвань • фрази) → Спрощене", "Convert Simplified and Traditional Chinese": "Конвертувати спрощену/традиційну китайську", - "Convert Mode": "Режим конвертації" + "Convert Mode": "Режим конвертації", + "Failed to auto-save book cover for lock screen: {{error}}": "Не вдалося автоматично зберегти обкладинку книги для екрана блокування: {{error}}" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 4720c85e..c9a13934 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "Phồn thể (Hồng Kông) → Giản thể", "Traditional (Taiwan) to Simplified, with phrases": "Phồn thể (Đài Loan • cụm từ) → Giản thể", "Convert Simplified and Traditional Chinese": "Chuyển đổi Trung giản/Phồn thể", - "Convert Mode": "Chế độ chuyển đổi" + "Convert Mode": "Chế độ chuyển đổi", + "Failed to auto-save book cover for lock screen: {{error}}": "Không thể tự động lưu bìa sách cho màn hình khóa: {{error}}" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index e7dcc7bb..5c6b7cc8 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "繁体(香港) → 简体", "Traditional (Taiwan) to Simplified, with phrases": "繁体(台湾) → 简体习惯用语", "Convert Simplified and Traditional Chinese": "简繁中文转换", - "Convert Mode": "转换模式" + "Convert Mode": "转换模式", + "Failed to auto-save book cover for lock screen: {{error}}": "自动保存锁屏书籍封面失败:{{error}}" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 2b33a948..23e86213 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -666,5 +666,6 @@ "Traditional (Hong Kong) to Simplified": "繁體(香港) → 簡體", "Traditional (Taiwan) to Simplified, with phrases": "繁體(台灣) → 簡體慣用語", "Convert Simplified and Traditional Chinese": "簡繁中文轉換", - "Convert Mode": "轉換模式" + "Convert Mode": "轉換模式", + "Failed to auto-save book cover for lock screen: {{error}}": "自動保存鎖定螢幕書籍封面失敗:{{error}}" } diff --git a/apps/readest-app/src-tauri/capabilities/default.json b/apps/readest-app/src-tauri/capabilities/default.json index 79bebce0..21bdb6e2 100644 --- a/apps/readest-app/src-tauri/capabilities/default.json +++ b/apps/readest-app/src-tauri/capabilities/default.json @@ -69,6 +69,9 @@ }, { "path": "**/Readest/**/*" + }, + { + "path": "**/last-book-cover.png" } ] }, diff --git a/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt b/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt index b3aa56af..d118c588 100644 --- a/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt +++ b/apps/readest-app/src-tauri/gen/android/app/src/main/java/com/bilingify/readest/MainActivity.kt @@ -6,6 +6,7 @@ import androidx.activity.enableEdgeToEdge import android.view.KeyEvent import android.webkit.WebView import android.util.Log +import android.content.Intent import android.graphics.Color import android.app.ActivityManager import android.content.res.Configuration @@ -13,6 +14,7 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import com.readest.native_bridge.KeyDownInterceptor +import com.readest.native_bridge.NativeBridgePlugin class MainActivity : TauriActivity(), KeyDownInterceptor { private lateinit var wv: WebView @@ -92,4 +94,10 @@ class MainActivity : TauriActivity(), KeyDownInterceptor { ) } } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + NativeBridgePlugin.getInstance()?.handleActivityResult(requestCode, resultCode, data) + } } diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt index 1bacc83b..1acdb24a 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/android/src/main/java/NativeBridgePlugin.kt @@ -8,6 +8,7 @@ import android.util.Log import android.os.Build import android.os.Environment import android.provider.Settings +import android.provider.DocumentsContract import android.view.View import android.view.KeyEvent import android.view.WindowInsets @@ -25,6 +26,8 @@ import androidx.core.content.FileProvider import androidx.core.content.ContextCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.browser.customtabs.CustomTabsIntent import app.tauri.annotation.Command import app.tauri.annotation.InvokeArg @@ -128,11 +131,16 @@ class NativeBridgePlugin(private val activity: Activity): Plugin(activity) { } companion object { - var pendingInvoke: Invoke? = null private const val REQUEST_MANAGE_STORAGE = 1001 + private const val FOLDER_PICKER_REQUEST_CODE = 1002 + var pendingInvoke: Invoke? = null + var pendingFolderPickerInvoke: Invoke? = null + private var instance: NativeBridgePlugin? = null + fun getInstance(): NativeBridgePlugin? = instance } override fun load(webView: WebView) { + instance = this super.load(webView) handleIntent(activity.intent) } @@ -682,4 +690,87 @@ class NativeBridgePlugin(private val activity: Activity): Plugin(activity) { invoke.reject("Failed to open URL: ${e.message}") } } + + @Command + fun select_directory(invoke: Invoke) { + pendingFolderPickerInvoke = invoke + + try { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + activity.startActivityForResult(intent, FOLDER_PICKER_REQUEST_CODE) + } catch (e: Exception) { + val result = JSObject() + result.put("cancelled", true) + result.put("uri", null) + result.put("path", null) + result.put("error", e.message) + invoke.resolve(result) + pendingFolderPickerInvoke = null + } + } + + fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == FOLDER_PICKER_REQUEST_CODE) { + val invoke = pendingFolderPickerInvoke + if (invoke != null) { + handleDirectorySelected(data?.data, invoke) + pendingFolderPickerInvoke = null + } + } + } + + private fun handleDirectorySelected(uri: Uri?, invoke: Invoke) { + val result = JSObject() + if (uri == null) { + result.put("cancelled", true) + result.put("uri", null) + result.put("path", null) + } else { + try { + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + activity.contentResolver.takePersistableUriPermission(uri, flags) + result.put("cancelled", false) + result.put("uri", uri.toString()) + result.put("path", extractPathFromUri(uri)) + } catch (e: SecurityException) { + result.put("cancelled", true) + result.put("uri", uri.toString()) + result.put("path", extractPathFromUri(uri)) + result.put("error", "Permission error: ${e.message}") + } catch (e: Exception) { + result.put("cancelled", true) + result.put("uri", null) + result.put("path", null) + result.put("error", "Error: ${e.message}") + } + } + + invoke.resolve(result) + pendingInvoke = null + } + + private fun extractPathFromUri(uri: Uri): String? { + val path = uri.path ?: return null + return try { + when { + DocumentsContract.isTreeUri(uri) -> { + val treeDocId = DocumentsContract.getTreeDocumentId(uri) + val split = treeDocId.split(":") + if (split[0].equals("primary", ignoreCase = true)) { + if (split.size > 1) { + Environment.getExternalStorageDirectory().path + "/" + split[1] + } else { + Environment.getExternalStorageDirectory().path + } + } else { + "/storage/${split[0]}/" + (if (split.size > 1) split[1] else "") + } + } + else -> null + } + } catch (e: Exception) { + path + } + } } diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/build.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/build.rs index 8e615e5a..10653122 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/build.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/build.rs @@ -19,6 +19,7 @@ const COMMANDS: &[&str] = &[ "set_screen_brightness", "get_external_sdcard_path", "open_external_url", + "select_directory", "request_manage_storage_permission", "check_permissions", "request_permissions", diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/commands/select_directory.toml b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/commands/select_directory.toml new file mode 100644 index 00000000..79ce86cd --- /dev/null +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/commands/select_directory.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-select-directory" +description = "Enables the select_directory command without any pre-configured scope." +commands.allow = ["select_directory"] + +[[permission]] +identifier = "deny-select-directory" +description = "Denies the select_directory command without any pre-configured scope." +commands.deny = ["select_directory"] diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/reference.md b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/reference.md index d08c0626..2fecfb0a 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/reference.md +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/autogenerated/reference.md @@ -24,6 +24,7 @@ Default permissions for the plugin - `allow-set-screen-brightness` - `allow-get-external-sdcard-path` - `allow-open-external-url` +- `allow-select-directory` - `allow-request-manage-storage-permission` - `allow-check-permissions` - `allow-request-permissions` @@ -666,6 +667,32 @@ Denies the request_permissions command without any pre-configured scope. +`native-bridge:allow-select-directory` + + + + +Enables the select_directory command without any pre-configured scope. + + + + + + + +`native-bridge:deny-select-directory` + + + + +Denies the select_directory command without any pre-configured scope. + + + + + + + `native-bridge:allow-set-screen-brightness` diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/default.toml b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/default.toml index d20ca78c..8eac4050 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/default.toml +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/default.toml @@ -21,6 +21,7 @@ permissions = [ "allow-set-screen-brightness", "allow-get-external-sdcard-path", "allow-open-external-url", + "allow-select-directory", "allow-request-manage-storage-permission", "allow-check-permissions", "allow-request-permissions", diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/schemas/schema.json b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/schemas/schema.json index 23756183..732e79e5 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/schemas/schema.json +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/permissions/schemas/schema.json @@ -582,6 +582,18 @@ "const": "deny-request-permissions", "markdownDescription": "Denies the request_permissions command without any pre-configured scope." }, + { + "description": "Enables the select_directory command without any pre-configured scope.", + "type": "string", + "const": "allow-select-directory", + "markdownDescription": "Enables the select_directory command without any pre-configured scope." + }, + { + "description": "Denies the select_directory command without any pre-configured scope.", + "type": "string", + "const": "deny-select-directory", + "markdownDescription": "Denies the select_directory command without any pre-configured scope." + }, { "description": "Enables the set_screen_brightness command without any pre-configured scope.", "type": "string", @@ -619,10 +631,10 @@ "markdownDescription": "Denies the use_background_audio command without any pre-configured scope." }, { - "description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`", + "description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`", "type": "string", "const": "default", - "markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`" + "markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`\n- `allow-get-safe-area-insets`\n- `allow-get-screen-brightness`\n- `allow-set-screen-brightness`\n- `allow-get-external-sdcard-path`\n- `allow-open-external-url`\n- `allow-select-directory`\n- `allow-request-manage-storage-permission`\n- `allow-check-permissions`\n- `allow-request-permissions`\n- `allow-checkPermissions`\n- `allow-requestPermissions`" } ] } diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/commands.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/commands.rs index 54ed1f57..8a4da421 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/commands.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/commands.rs @@ -157,6 +157,13 @@ pub(crate) async fn open_external_url( app.native_bridge().open_external_url(payload) } +#[command] +pub(crate) async fn select_directory( + app: AppHandle, +) -> Result { + app.native_bridge().select_directory() +} + #[command] pub(crate) async fn request_manage_storage_permission( app: AppHandle, diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/desktop.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/desktop.rs index 84eefae3..e23e9f2a 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/desktop.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/desktop.rs @@ -129,6 +129,10 @@ impl NativeBridge { Err(crate::Error::UnsupportedPlatformError) } + pub fn select_directory(&self) -> crate::Result { + Err(crate::Error::UnsupportedPlatformError) + } + pub fn request_manage_storage_permission( &self, ) -> crate::Result { diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/lib.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/lib.rs index 1c6cf0a3..0d6ee5ac 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/lib.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/lib.rs @@ -57,6 +57,7 @@ pub fn init() -> TauriPlugin { commands::set_screen_brightness, commands::get_external_sdcard_path, commands::open_external_url, + commands::select_directory, commands::request_manage_storage_permission, ]) .setup(|app, api| { diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/mobile.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/mobile.rs index 99a1cc2d..c4af527e 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/mobile.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/mobile.rs @@ -208,6 +208,14 @@ impl NativeBridge { } } +impl NativeBridge { + pub fn select_directory(&self) -> crate::Result { + self.0 + .run_mobile_plugin("select_directory", ()) + .map_err(Into::into) + } +} + impl NativeBridge { pub fn request_manage_storage_permission( &self, diff --git a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/models.rs b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/models.rs index e11c9e7e..b591269a 100644 --- a/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/models.rs +++ b/apps/readest-app/src-tauri/plugins/tauri-plugin-native-bridge/src/models.rs @@ -215,3 +215,12 @@ pub struct OpenExternalUrlResponse { pub success: bool, pub error: Option, } + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SelectDirectoryResponse { + pub cancelled: Option, + pub uri: Option, + pub path: Option, + pub error: Option, +} diff --git a/apps/readest-app/src/app/library/components/SettingsMenu.tsx b/apps/readest-app/src/app/library/components/SettingsMenu.tsx index 61e0b2f3..896570f0 100644 --- a/apps/readest-app/src/app/library/components/SettingsMenu.tsx +++ b/apps/readest-app/src/app/library/components/SettingsMenu.tsx @@ -21,6 +21,7 @@ import { optInTelemetry, optOutTelemetry } from '@/utils/telemetry'; import { setAboutDialogVisible } from '@/components/AboutWindow'; import { setMigrateDataDirDialogVisible } from '@/app/library/components/MigrateDataWindow'; import { saveSysSettings } from '@/helpers/settings'; +import { selectDirectory } from '@/utils/bridge'; import UserAvatar from '@/components/UserAvatar'; import MenuItem from '@/components/MenuItem'; import Quota from '@/components/Quota'; @@ -32,6 +33,7 @@ interface SettingsMenuProps { interface Permissions { postNotification: PermissionState; + manageStorage: PermissionState; } const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { @@ -172,8 +174,22 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { setSettingsDialogOpen(true); }; - const handleSetSavedBookCoverForLockScreen = () => { + const handleSetSavedBookCoverForLockScreen = async () => { + let permission = await invoke('plugin:native-bridge|checkPermissions'); + if (permission.manageStorage !== 'granted') { + permission = await invoke( + 'plugin:native-bridge|request_manage_storage_permission', + ); + } + if (permission.manageStorage !== 'granted' && appService?.distChannel === 'readest') return; + const newValue = settings.savedBookCoverForLockScreen ? '' : 'default'; + if (newValue) { + const response = await selectDirectory(); + if (response.path) { + saveSysSettings(envConfig, 'savedBookCoverForLockScreenPath', response.path); + } + } saveSysSettings(envConfig, 'savedBookCoverForLockScreen', newValue); setSavedBookCoverForLockScreen(newValue); }; @@ -205,6 +221,10 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { ? _('Light Mode') : _('Auto Mode'); + const savedBookCoverPath = settings.savedBookCoverForLockScreenPath; + const coverDir = savedBookCoverPath ? savedBookCoverPath.split('/').pop() : 'Images'; + const savedBookCoverDescription = `💾 ${coverDir}/last-book-cover.png`; + return ( = ({ setIsDropdownOpen }) => { diff --git a/apps/readest-app/src/app/reader/hooks/useAutoSaveBookCover.ts b/apps/readest-app/src/app/reader/hooks/useAutoSaveBookCover.ts index 63d6fe10..ee33c1b0 100644 --- a/apps/readest-app/src/app/reader/hooks/useAutoSaveBookCover.ts +++ b/apps/readest-app/src/app/reader/hooks/useAutoSaveBookCover.ts @@ -1,11 +1,14 @@ import { useCallback, useEffect } from 'react'; import { useEnv } from '@/context/EnvContext'; +import { useTranslation } from '@/hooks/useTranslation'; import { useBookDataStore } from '@/store/bookDataStore'; import { useSettingsStore } from '@/store/settingsStore'; import { throttle } from '@/utils/throttle'; import { getCoverFilename } from '@/utils/book'; +import { eventDispatcher } from '@/utils/event'; export const useBookCoverAutoSave = (bookKey: string) => { + const _ = useTranslation(); const { envConfig, appService } = useEnv(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -17,15 +20,31 @@ export const useBookCoverAutoSave = (bookKey: string) => { const bookData = useBookDataStore.getState().getBookData(bookKey); const book = bookData?.book; const savedBookHash = settings.savedBookCoverForLockScreen; + const savedCoverPath = settings.savedBookCoverForLockScreenPath; if (appService && book && savedBookHash && savedBookHash !== book?.hash) { const coverPath = await appService.resolveFilePath(getCoverFilename(book), 'Books'); try { - await appService.copyFile(coverPath, 'last-book-cover.png', 'Images'); + const lastCoverFilename = 'last-book-cover.png'; + const builtinImagesPath = await appService.resolveFilePath('', 'Images'); + if (!savedCoverPath || savedCoverPath === builtinImagesPath) { + await appService.copyFile(coverPath, lastCoverFilename, 'Images'); + } else { + await appService.copyFile( + coverPath, + `${savedCoverPath}/${lastCoverFilename}`, + 'None', + ); + } settings.savedBookCoverForLockScreen = book.hash; useSettingsStore.getState().setSettings(settings); useSettingsStore.getState().saveSettings(envConfig, settings); } catch (error) { - console.error('Failed to auto-save book cover for lock screen:', error); + eventDispatcher.dispatch('toast', { + type: 'error', + message: _('Failed to auto-save book cover for lock screen: {{error}}', { + error: error instanceof Error ? error.message : String(error), + }), + }); } } }, 5000); diff --git a/apps/readest-app/src/services/nativeAppService.ts b/apps/readest-app/src/services/nativeAppService.ts index da99a262..34b93bfd 100644 --- a/apps/readest-app/src/services/nativeAppService.ts +++ b/apps/readest-app/src/services/nativeAppService.ts @@ -224,8 +224,12 @@ export const nativeFileSystem: FileSystem = { } }, async copyFile(srcPath: string, dstPath: string, base: BaseDir) { - if (!(await this.exists(getDirPath(dstPath), base))) { - await this.createDir(getDirPath(dstPath), base, true); + try { + if (!(await this.exists(getDirPath(dstPath), base))) { + await this.createDir(getDirPath(dstPath), base, true); + } + } catch (error) { + console.log('Failed to create directory for copying file:', error); } if (isContentURI(srcPath)) { const prefix = await this.getPrefix(base); diff --git a/apps/readest-app/src/types/settings.ts b/apps/readest-app/src/types/settings.ts index 9497503e..cff52468 100644 --- a/apps/readest-app/src/types/settings.ts +++ b/apps/readest-app/src/types/settings.ts @@ -57,6 +57,7 @@ export interface SystemSettings { lastOpenBooks: string[]; autoImportBooksOnOpen: boolean; savedBookCoverForLockScreen: string; + savedBookCoverForLockScreenPath: string; telemetryEnabled: boolean; libraryViewMode: LibraryViewModeType; librarySortBy: LibrarySortByType; diff --git a/apps/readest-app/src/utils/bridge.ts b/apps/readest-app/src/utils/bridge.ts index 78a1114f..cec7068d 100644 --- a/apps/readest-app/src/utils/bridge.ts +++ b/apps/readest-app/src/utils/bridge.ts @@ -84,6 +84,13 @@ interface GetExternalSDCardPathResponse { error?: string; } +interface SelectDirectoryResponse { + cancelled?: boolean; + uri?: string; + path?: string; + error?: string; +} + export async function copyURIToPath(request: CopyURIRequest): Promise { const result = await invoke('plugin:native-bridge|copy_uri_to_path', { payload: request, @@ -190,3 +197,8 @@ export async function getExternalSDCardPath(): Promise { + const result = await invoke('plugin:native-bridge|select_directory'); + return result; +}