From bc2c3a2c9a2a5f61bda235d4bc65dfdf6034f8d4 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Thu, 26 Jun 2025 15:59:34 +0800 Subject: [PATCH] feat: add options to reset settings in the config dialog, closes #1111 (#1475) --- .../public/locales/ar/translation.json | 4 +- .../public/locales/de/translation.json | 4 +- .../public/locales/el/translation.json | 4 +- .../public/locales/es/translation.json | 4 +- .../public/locales/fr/translation.json | 4 +- .../public/locales/hi/translation.json | 4 +- .../public/locales/id/translation.json | 4 +- .../public/locales/it/translation.json | 4 +- .../public/locales/ja/translation.json | 4 +- .../public/locales/ko/translation.json | 4 +- .../public/locales/nl/translation.json | 4 +- .../public/locales/pl/translation.json | 4 +- .../public/locales/pt/translation.json | 4 +- .../public/locales/ru/translation.json | 4 +- .../public/locales/tr/translation.json | 4 +- .../public/locales/uk/translation.json | 4 +- .../public/locales/vi/translation.json | 4 +- .../public/locales/zh-CN/translation.json | 4 +- .../public/locales/zh-TW/translation.json | 4 +- .../reader/components/settings/ColorPanel.tsx | 22 ++++- .../components/settings/ControlPanel.tsx | 24 +++++- .../reader/components/settings/DialogMenu.tsx | 48 ++++++----- .../reader/components/settings/FontPanel.tsx | 27 ++++++- .../reader/components/settings/LangPanel.tsx | 35 ++++++-- .../components/settings/LayoutPanel.tsx | 44 +++++++++- .../reader/components/settings/MiscPanel.tsx | 26 +++++- .../components/settings/SettingsDialog.tsx | 81 ++++++++++++++++--- .../src/app/reader/hooks/useBookShortcuts.ts | 4 +- .../src/app/reader/hooks/useResetSettings.ts | 28 +++++++ apps/readest-app/src/components/Dropdown.tsx | 2 +- apps/readest-app/src/components/MenuItem.tsx | 3 + apps/readest-app/src/services/appService.ts | 38 ++++----- apps/readest-app/src/types/book.ts | 3 +- apps/readest-app/src/types/system.ts | 3 +- 34 files changed, 372 insertions(+), 92 deletions(-) create mode 100644 apps/readest-app/src/app/reader/hooks/useResetSettings.ts diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 091b516c..0514c6cb 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -381,5 +381,7 @@ "Enter CSS for reader interface styling...": "أدخل CSS لتنسيق واجهة القارئ...", "Crop": "قص", "Book Covers": "أغلفة الكتب", - "Fit": "ملاءمة" + "Fit": "ملاءمة", + "Reset {{settings}}": "إعادة تعيين {{settings}}", + "Reset Settings": "إعادة تعيين الإعدادات" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index 029c5bce..987b5adb 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -369,5 +369,7 @@ "Enter CSS for reader interface styling...": "CSS für die Leseroberfläche eingeben...", "Crop": "Zuschneiden", "Book Covers": "Buchcover", - "Fit": "Anpassen" + "Fit": "Anpassen", + "Reset {{settings}}": "{{settings}} zurücksetzen", + "Reset Settings": "Einstellungen zurücksetzen" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 950b7a01..8f382c4a 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -369,5 +369,7 @@ "Enter CSS for reader interface styling...": "Εισαγάγετε CSS για στυλ διεπαφής αναγνώστη...", "Crop": "Περικοπή", "Book Covers": "Εξώφυλλα βιβλίων", - "Fit": "Προσαρμογή" + "Fit": "Προσαρμογή", + "Reset {{settings}}": "Επαναφορά {{settings}}", + "Reset Settings": "Επαναφορά ρυθμίσεων" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 105b50fb..9193937c 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -372,5 +372,7 @@ "Enter CSS for reader interface styling...": "Introduce CSS para la interfaz de lectura...", "Crop": "Recortar", "Book Covers": "Portadas de libros", - "Fit": "Encajar" + "Fit": "Encajar", + "Reset {{settings}}": "Restablecer {{settings}}", + "Reset Settings": "Restablecer configuración" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index e2de1de5..ff848461 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -372,5 +372,7 @@ "Enter CSS for reader interface styling...": "Saisissez le CSS pour l’interface de lecture...", "Crop": "Rogner", "Book Covers": "Couvertures de livres", - "Fit": "Adapter" + "Fit": "Adapter", + "Reset {{settings}}": "Réinitialiser {{settings}}", + "Reset Settings": "Réinitialiser les paramètres" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index 7c4ca3f2..bbabf64b 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -369,5 +369,7 @@ "Enter CSS for reader interface styling...": "रीडर इंटरफ़ेस के लिए CSS दर्ज करें...", "Crop": "काटें", "Book Covers": "पुस्तक कवर", - "Fit": "फिट" + "Fit": "फिट", + "Reset {{settings}}": "रीसेट {{settings}}", + "Reset Settings": "सेटिंग्स रीसेट करें" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index f04890cd..dda0bfcf 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "Masukkan CSS untuk gaya antarmuka pembaca...", "Crop": "Potong", "Book Covers": "Sampul Buku", - "Fit": "Pas" + "Fit": "Pas", + "Reset {{settings}}": "Atur Ulang {{settings}}", + "Reset Settings": "Atur Ulang Pengaturan" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index f3883648..9c8ee536 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -372,5 +372,7 @@ "Enter CSS for reader interface styling...": "Inserisci il CSS per l'interfaccia di lettura...", "Crop": "Ritaglia", "Book Covers": "Copertine libri", - "Fit": "Adatta" + "Fit": "Adatta", + "Reset {{settings}}": "Reimposta {{settings}}", + "Reset Settings": "Reimposta impostazioni" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 0211e140..6e136167 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "リーダーインターフェース用CSSを入力...", "Crop": "トリミング", "Book Covers": "書籍の表紙", - "Fit": "フィット" + "Fit": "フィット", + "Reset {{settings}}": "{{settings}}をリセット", + "Reset Settings": "設定をリセット" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index a6948b79..8f9d1dab 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "리더 인터페이스 스타일용 CSS 입력...", "Crop": "자르기", "Book Covers": "책 표지", - "Fit": "맞춤" + "Fit": "맞춤", + "Reset {{settings}}": "{{settings}} 재설정", + "Reset Settings": "설정 재설정" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index 79e5e8c8..5328c011 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -369,5 +369,7 @@ "Enter CSS for reader interface styling...": "Voer CSS in voor de opmaak van de leesinterface...", "Crop": "Bijsnijden", "Book Covers": "Boekomslagen", - "Fit": "Passend maken" + "Fit": "Passend maken", + "Reset {{settings}}": "Reset {{settings}}", + "Reset Settings": "Instellingen resetten" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 534d9596..b0e33e0d 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -375,5 +375,7 @@ "Enter CSS for reader interface styling...": "Wprowadź CSS dla stylu interfejsu czytnika...", "Crop": "Przytnij", "Book Covers": "Okładki książek", - "Fit": "Pasuje" + "Fit": "Pasuje", + "Reset {{settings}}": "Zresetuj {{settings}}", + "Reset Settings": "Zresetuj ustawienia" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 7222b526..53807d12 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -372,5 +372,7 @@ "Enter CSS for reader interface styling...": "Insira o CSS para o estilo da interface de leitura...", "Crop": "Cortar", "Book Covers": "Capas de Livros", - "Fit": "Encaixar" + "Fit": "Encaixar", + "Reset {{settings}}": "Redefinir {{settings}}", + "Reset Settings": "Redefinir Configurações" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 0499ce75..67ad4232 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -375,5 +375,7 @@ "Enter CSS for reader interface styling...": "Введите CSS для оформления интерфейса ридера...", "Crop": "Обрезать", "Book Covers": "Обложки книг", - "Fit": "Подогнать" + "Fit": "Подогнать", + "Reset {{settings}}": "Сбросить {{settings}}", + "Reset Settings": "Сбросить настройки" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index fff0d9bd..16268658 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -369,5 +369,7 @@ "Enter CSS for reader interface styling...": "Okuyucu arayüzü stili için CSS girin...", "Crop": "Kes", "Book Covers": "Kitap Kapağı", - "Fit": "Uygun" + "Fit": "Uygun", + "Reset {{settings}}": "{{settings}}'i Sıfırla", + "Reset Settings": "Ayarları Sıfırla" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index 2993169a..a561bc44 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -375,5 +375,7 @@ "Enter CSS for reader interface styling...": "Введіть CSS для стилізації інтерфейсу рідера...", "Crop": "Обрізати", "Book Covers": "Обкладинки книг", - "Fit": "Вписати" + "Fit": "Вписати", + "Reset {{settings}}": "Скинути {{settings}}", + "Reset Settings": "Скинути налаштування" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index fcc1e200..5fcec4bb 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "Nhập CSS cho giao diện trình đọc...", "Crop": "Cắt", "Book Covers": "Bìa sách", - "Fit": "Vừa vặn" + "Fit": "Vừa vặn", + "Reset {{settings}}": "Đặt lại {{settings}}", + "Reset Settings": "Đặt lại cài đặt" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index 309394b2..e042d8ee 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "输入阅读器界面样式的CSS…", "Crop": "裁剪", "Book Covers": "书籍封面", - "Fit": "适应" + "Fit": "适应", + "Reset {{settings}}": "重置{{settings}}", + "Reset Settings": "重置设置" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index b5c288e6..754e7c69 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -366,5 +366,7 @@ "Enter CSS for reader interface styling...": "輸入閱讀器介面樣式的CSS…", "Crop": "裁剪", "Book Covers": "書籍封面", - "Fit": "適應" + "Fit": "適應", + "Reset {{settings}}": "重置{{settings}}", + "Reset Settings": "重置設置" } diff --git a/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx b/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx index 8a6e3a47..e2338874 100644 --- a/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx @@ -18,12 +18,14 @@ import { useReaderStore } from '@/store/readerStore'; import { useTranslation } from '@/hooks/useTranslation'; import { useSettingsStore } from '@/store/settingsStore'; import { useResponsiveSize } from '@/hooks/useResponsiveSize'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; import { CODE_LANGUAGES, CodeLanguage, manageSyntaxHighlighting } from '@/utils/highlightjs'; +import { SettingsPanelPanelProp } from './SettingsDialog'; import Select from '@/components/Select'; import ThemeEditor from './ThemeEditor'; -const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const ColorPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { themeMode, themeColor, isDarkMode, setThemeMode, setThemeColor, saveCustomTheme } = useThemeStore(); @@ -44,6 +46,24 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const [codeHighlighting, setcodeHighlighting] = useState(viewSettings.codeHighlighting!); const [codeLanguage, setCodeLanguage] = useState(viewSettings.codeLanguage!); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + overrideColor: setOverrideColor, + invertImgColorInDark: setInvertImgColorInDark, + codeHighlighting: setcodeHighlighting, + codeLanguage: setCodeLanguage, + }); + setThemeColor('default'); + setThemeMode('auto'); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (invertImgColorInDark === viewSettings.invertImgColorInDark) return; saveViewSettings(envConfig, bookKey, 'invertImgColorInDark', invertImgColorInDark); diff --git a/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx b/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx index 958270b2..c85185e2 100644 --- a/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx @@ -8,9 +8,11 @@ import { getStyles } from '@/utils/style'; import { getMaxInlineSize } from '@/utils/config'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; import { RELOAD_BEFREE_SAVED_TIMEOUT_MS } from '@/services/constants'; +import { SettingsPanelPanelProp } from './SettingsDialog'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; import NumberInput from './NumberInput'; -const ControlPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const ControlPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { envConfig, appService } = useEnv(); const { getView, getViewSettings } = useReaderStore(); @@ -28,6 +30,26 @@ const ControlPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const [animated, setAnimated] = useState(viewSettings.animated!); const [allowScript, setAllowScript] = useState(viewSettings.allowScript!); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + scrolled: setScrolledMode, + continuousScroll: setIsContinuousScroll, + scrollingOverlap: setScrollingOverlap, + volumeKeysToFlip: setVolumeKeysToFlip, + disableClick: setIsDisableClick, + swapClickArea: setSwapClickArea, + animated: setAnimated, + allowScript: setAllowScript, + }); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (isScrolledMode === viewSettings.scrolled) return; saveViewSettings(envConfig, bookKey, 'scrolled', isScrolledMode); diff --git a/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx b/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx index 55c67aad..95460038 100644 --- a/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx +++ b/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx @@ -3,20 +3,30 @@ import React from 'react'; import { MdCheck } from 'react-icons/md'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; -import { useDefaultIconSize } from '@/hooks/useResponsiveSize'; +import { useResponsiveSize } from '@/hooks/useResponsiveSize'; +import { SettingsPanelType } from './SettingsDialog'; +import MenuItem from '@/components/MenuItem'; interface DialogMenuProps { - toggleDropdown?: () => void; + activePanel: SettingsPanelType; + setIsDropdownOpen?: (open: boolean) => void; + onReset: () => void; + resetLabel?: string; } -const DialogMenu: React.FC = ({ toggleDropdown }) => { +const DialogMenu: React.FC = ({ setIsDropdownOpen, onReset, resetLabel }) => { const _ = useTranslation(); - const iconSize = useDefaultIconSize(); + const iconSize = useResponsiveSize(16); const { isFontLayoutSettingsGlobal, setFontLayoutSettingsGlobal } = useSettingsStore(); const handleToggleGlobal = () => { setFontLayoutSettingsGlobal(!isFontLayoutSettingsGlobal); - toggleDropdown?.(); + setIsDropdownOpen?.(false); + }; + + const handleResetToDefaults = () => { + onReset(); + setIsDropdownOpen?.(false); }; return ( @@ -27,24 +37,18 @@ const DialogMenu: React.FC = ({ toggleDropdown }) => { 'text-base sm:text-sm', )} > - + /> + ); }; diff --git a/apps/readest-app/src/app/reader/components/settings/FontPanel.tsx b/apps/readest-app/src/app/reader/components/settings/FontPanel.tsx index 1b25aeb5..ed7dd261 100644 --- a/apps/readest-app/src/app/reader/components/settings/FontPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/FontPanel.tsx @@ -24,6 +24,8 @@ import { getOSPlatform, isCJKEnv } from '@/utils/misc'; import { getSysFontsList } from '@/utils/bridge'; import { isTauriAppPlatform } from '@/services/environment'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; +import { SettingsPanelPanelProp } from './SettingsDialog'; import NumberInput from './NumberInput'; import FontDropdown from './FontDropDown'; @@ -79,7 +81,7 @@ const FontFace = ({ ); }; -const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const FontPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { envConfig } = useEnv(); const { getView, getViewSettings } = useReaderStore(); @@ -119,10 +121,10 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { break; } const [sysFonts, setSysFonts] = useState(defaultSysFonts); + const [defaultFont, setDefaultFont] = useState(viewSettings.defaultFont!); const [defaultFontSize, setDefaultFontSize] = useState(viewSettings.defaultFontSize!); const [minFontSize, setMinFontSize] = useState(viewSettings.minimumFontSize!); const [overrideFont, setOverrideFont] = useState(viewSettings.overrideFont!); - const [defaultFont, setDefaultFont] = useState(viewSettings.defaultFont!); const [defaultCJKFont, setDefaultCJKFont] = useState(viewSettings.defaultCJKFont!); const [serifFont, setSerifFont] = useState(viewSettings.serifFont!); const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont!); @@ -132,6 +134,27 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { return genCJKFontsList(sysFonts); }); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + defaultFont: setDefaultFont, + defaultFontSize: setDefaultFontSize, + minimumFontSize: setMinFontSize, + overrideFont: setOverrideFont, + defaultCJKFont: setDefaultCJKFont, + serifFont: setSerifFont, + sansSerifFont: setSansSerifFont, + monospaceFont: setMonospaceFont, + fontWeight: setFontWeight, + }); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { setCJKFonts((prev) => { const newFonts = genCJKFontsList(sysFonts); diff --git a/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx b/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx index d8737b81..51878a55 100644 --- a/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx @@ -8,20 +8,39 @@ import { useTranslation } from '@/hooks/useTranslation'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; import { getTranslators } from '@/services/translators'; import { TRANSLATED_LANGS } from '@/services/constants'; +import { SettingsPanelPanelProp } from './SettingsDialog'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; import { initDayjs } from '@/utils/time'; import Select from '@/components/Select'; -const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const LangPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { token } = useAuth(); const { envConfig } = useEnv(); const { getViewSettings, setViewSettings } = useReaderStore(); const viewSettings = getViewSettings(bookKey)!; + const [uiLanguage, setUILanguage] = useState(viewSettings.uiLanguage!); const [translationEnabled, setTranslationEnabled] = useState(viewSettings.translationEnabled!); const [translationProvider, setTranslationProvider] = useState(viewSettings.translationProvider!); const [translateTargetLang, setTranslateTargetLang] = useState(viewSettings.translateTargetLang!); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + uiLanguage: setUILanguage, + translationEnabled: setTranslationEnabled, + translationProvider: setTranslationProvider, + translateTargetLang: setTranslateTargetLang, + }); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const getCurrentUILangOption = () => { const uiLanguage = viewSettings.uiLanguage; return { @@ -43,10 +62,7 @@ const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const handleSelectUILang = (event: React.ChangeEvent) => { const option = event.target.value; - saveViewSettings(envConfig, bookKey, 'uiLanguage', option, false, false); - const locale = option ? option : navigator.language; - i18n.changeLanguage(locale); - initDayjs(locale); + setUILanguage(option); }; const getTranslationProviderOptions = () => { @@ -97,6 +113,15 @@ const LangPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { setViewSettings(bookKey, { ...viewSettings }); }; + useEffect(() => { + if (uiLanguage === viewSettings.uiLanguage) return; + saveViewSettings(envConfig, bookKey, 'uiLanguage', uiLanguage, false, false); + const locale = uiLanguage ? uiLanguage : navigator.language; + i18n.changeLanguage(locale); + initDayjs(locale); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [uiLanguage]); + useEffect(() => { if (translationEnabled === viewSettings.translationEnabled) return; saveViewSettings(envConfig, bookKey, 'translationEnabled', translationEnabled, true, false); diff --git a/apps/readest-app/src/app/reader/components/settings/LayoutPanel.tsx b/apps/readest-app/src/app/reader/components/settings/LayoutPanel.tsx index aba1ad7c..71770b78 100644 --- a/apps/readest-app/src/app/reader/components/settings/LayoutPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/LayoutPanel.tsx @@ -8,6 +8,7 @@ import { useEnv } from '@/context/EnvContext'; import { useReaderStore } from '@/store/readerStore'; import { useBookDataStore } from '@/store/bookDataStore'; import { useTranslation } from '@/hooks/useTranslation'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; import { isCJKEnv } from '@/utils/misc'; import { getStyles } from '@/utils/style'; import { getMaxInlineSize } from '@/utils/config'; @@ -15,9 +16,10 @@ import { lockScreenOrientation } from '@/utils/bridge'; import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book'; import { MIGHT_BE_RTL_LANGS, RELOAD_BEFREE_SAVED_TIMEOUT_MS } from '@/services/constants'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; +import { SettingsPanelPanelProp } from './SettingsDialog'; import NumberInput from './NumberInput'; -const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const LayoutPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { envConfig, appService } = useEnv(); const { getView, getViewSettings, getGridInsets, setViewSettings } = useReaderStore(); @@ -63,9 +65,47 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll!); const [showRemainingTime, setShowRemainingTime] = useState(viewSettings.showRemainingTime!); const [showPageNumber, setShowPageNumber] = useState(viewSettings.showPageNumber!); - const [screenOrientation, setScreenOrientation] = useState(viewSettings.screenOrientation!); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + paragraphMargin: setParagraphMargin, + lineHeight: setLineHeight, + wordSpacing: setWordSpacing, + letterSpacing: setLetterSpacing, + textIndent: setTextIndent, + fullJustification: setFullJustification, + hyphenation: setHyphenation, + marginTopPx: setMarginTopPx, + marginBottomPx: setMarginBottomPx, + marginLeftPx: setMarginLeftPx, + marginRightPx: setMarginRightPx, + compactMarginTopPx: setCompactMarginTopPx, + compactMarginBottomPx: setCompactMarginBottomPx, + compactMarginLeftPx: setCompactMarginLeftPx, + compactMarginRightPx: setCompactMarginRightPx, + gapPercent: setGapPercent, + maxColumnCount: setMaxColumnCount, + maxInlineSize: setMaxInlineSize, + maxBlockSize: setMaxBlockSize, + overrideLayout: setOverrideLayout, + doubleBorder: setDoubleBorder, + borderColor: setBorderColor, + showHeader: setShowHeader, + showFooter: setShowFooter, + showBarsOnScroll: setShowBarsOnScroll, + showRemainingTime: setShowRemainingTime, + showPageNumber: setShowPageNumber, + }); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { saveViewSettings(envConfig, bookKey, 'paragraphMargin', paragraphMargin); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/apps/readest-app/src/app/reader/components/settings/MiscPanel.tsx b/apps/readest-app/src/app/reader/components/settings/MiscPanel.tsx index 696a8939..33b39332 100644 --- a/apps/readest-app/src/app/reader/components/settings/MiscPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/MiscPanel.tsx @@ -1,17 +1,19 @@ import clsx from 'clsx'; import cssbeautify from 'cssbeautify'; -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useEnv } from '@/context/EnvContext'; import { useReaderStore } from '@/store/readerStore'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; +import { SettingsPanelPanelProp } from './SettingsDialog'; import { getStyles } from '@/utils/style'; import cssValidate from '@/utils/css'; type CSSType = 'book' | 'reader'; -const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { +const MiscPanel: React.FC = ({ bookKey, onRegisterReset }) => { const _ = useTranslation(); const { appService } = useEnv(); const { settings, isFontLayoutSettingsGlobal, setSettings } = useSettingsStore(); @@ -29,6 +31,22 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const contentTextareaRef = useRef(null); const uiTextareaRef = useRef(null); + const resetToDefaults = useResetViewSettings(); + + const handleReset = () => { + resetToDefaults({ + userStylesheet: setDraftContentStylesheet, + userUIStylesheet: setDraftUIStylesheet, + }); + applyStyles('book', true); + applyStyles('reader', true); + }; + + useEffect(() => { + onRegisterReset(handleReset); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const validateCSS = (cssInput: string): { isValid: boolean; error?: string } => { if (!cssInput.trim()) return { isValid: true }; @@ -64,9 +82,9 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { } }; - const applyStyles = (type: CSSType) => { + const applyStyles = (type: CSSType, clear = false) => { const cssInput = type === 'book' ? draftContentStylesheet : draftUIStylesheet; - const formattedCSS = cssbeautify(cssInput, { + const formattedCSS = cssbeautify(clear ? '' : cssInput, { indent: ' ', openbrace: 'end-of-line', autosemicolon: true, diff --git a/apps/readest-app/src/app/reader/components/settings/SettingsDialog.tsx b/apps/readest-app/src/app/reader/components/settings/SettingsDialog.tsx index 11181d99..2f74caff 100644 --- a/apps/readest-app/src/app/reader/components/settings/SettingsDialog.tsx +++ b/apps/readest-app/src/app/reader/components/settings/SettingsDialog.tsx @@ -22,7 +22,11 @@ import ControlPanel from './ControlPanel'; import LangPanel from './LangPanel'; import MiscPanel from './MiscPanel'; -type SettingsPanelType = 'Font' | 'Layout' | 'Color' | 'Control' | 'Language' | 'Custom'; +export type SettingsPanelType = 'Font' | 'Layout' | 'Color' | 'Control' | 'Language' | 'Custom'; +export type SettingsPanelPanelProp = { + bookKey: string; + onRegisterReset: (resetFn: () => void) => void; +}; type TabConfig = { tab: SettingsPanelType; @@ -84,6 +88,28 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo localStorage.setItem('lastConfigPanel', tab); }; + const [resetFunctions, setResetFunctions] = useState< + Record void) | null> + >({ + Font: null, + Layout: null, + Color: null, + Control: null, + Language: null, + Custom: null, + }); + + const registerResetFunction = (panel: SettingsPanelType, resetFn: () => void) => { + setResetFunctions((prev) => ({ ...prev, [panel]: resetFn })); + }; + + const handleResetCurrentPanel = () => { + const resetFn = resetFunctions[activePanel]; + if (resetFn) { + resetFn(); + } + }; + const handleClose = () => { setFontLayoutSettingsDialogOpen(false); }; @@ -130,6 +156,8 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo }; }, []); + const currentPanel = tabConfig.find((tab) => tab.tab === activePanel); + return ( = ({ boo header={
- {tabConfig.find((tab) => tab.tab === activePanel)?.label || ''} + {currentPanel?.label || ''}
} > - {activePanel === 'Font' && } - {activePanel === 'Layout' && } - {activePanel === 'Color' && } - {activePanel === 'Control' && } - {activePanel === 'Language' && } - {activePanel === 'Custom' && } + {activePanel === 'Font' && ( + registerResetFunction('Font', fn)} /> + )} + {activePanel === 'Layout' && ( + registerResetFunction('Layout', fn)} + /> + )} + {activePanel === 'Color' && ( + registerResetFunction('Color', fn)} + /> + )} + {activePanel === 'Control' && ( + registerResetFunction('Control', fn)} + /> + )} + {activePanel === 'Language' && ( + registerResetFunction('Language', fn)} + /> + )} + {activePanel === 'Custom' && ( + registerResetFunction('Custom', fn)} + /> + )}
); }; diff --git a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts index 46dcc318..ce5843d6 100644 --- a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts +++ b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts @@ -1,8 +1,6 @@ import { useReaderStore } from '@/store/readerStore'; import { useNotebookStore } from '@/store/notebookStore'; import { isTauriAppPlatform } from '@/services/environment'; -import useShortcuts from '@/hooks/useShortcuts'; -import useBooksManager from './useBooksManager'; import { useSidebarStore } from '@/store/sidebarStore'; import { useSettingsStore } from '@/store/settingsStore'; import { getStyles } from '@/utils/style'; @@ -10,6 +8,8 @@ import { tauriHandleToggleFullScreen, tauriQuitApp } from '@/utils/window'; import { eventDispatcher } from '@/utils/event'; import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ZOOM_STEP } from '@/services/constants'; import { viewPagination } from './usePagination'; +import useShortcuts from '@/hooks/useShortcuts'; +import useBooksManager from './useBooksManager'; interface UseBookShortcutsProps { sideBarBookKey: string | null; diff --git a/apps/readest-app/src/app/reader/hooks/useResetSettings.ts b/apps/readest-app/src/app/reader/hooks/useResetSettings.ts new file mode 100644 index 00000000..cdc944fc --- /dev/null +++ b/apps/readest-app/src/app/reader/hooks/useResetSettings.ts @@ -0,0 +1,28 @@ +import { Dispatch, SetStateAction } from 'react'; +import { useEnv } from '@/context/EnvContext'; +import { ViewSettings } from '@/types/book'; + +type SetterKey = keyof ViewSettings; +type SetterValue = SetStateAction & SetStateAction & SetStateAction; + +type StateSetters = Partial<{ + [Key in SetterKey]: Dispatch; +}>; + +export const useResetViewSettings = () => { + const { appService } = useEnv(); + + const resetToDefaults = (setters: StateSetters) => { + if (!appService) return; + const defaultSettings = appService.getDefaultViewSettings(); + + Object.entries(setters).forEach(([settingKey, setter]) => { + const freshValue = defaultSettings[settingKey as SetterKey]; + if (freshValue !== undefined) { + setter(freshValue as SetterValue); + } + }); + }; + + return resetToDefaults; +}; diff --git a/apps/readest-app/src/components/Dropdown.tsx b/apps/readest-app/src/components/Dropdown.tsx index 22bae5f0..995d7179 100644 --- a/apps/readest-app/src/components/Dropdown.tsx +++ b/apps/readest-app/src/components/Dropdown.tsx @@ -36,7 +36,7 @@ const Dropdown: React.FC = ({ : children; return ( -
+
{isOpen && (
setIsDropdownOpen(false)} /> )} diff --git a/apps/readest-app/src/components/MenuItem.tsx b/apps/readest-app/src/components/MenuItem.tsx index 2906ea27..37baf8d5 100644 --- a/apps/readest-app/src/components/MenuItem.tsx +++ b/apps/readest-app/src/components/MenuItem.tsx @@ -6,6 +6,7 @@ import { useResponsiveSize } from '@/hooks/useResponsiveSize'; interface MenuItemProps { label: string; description?: string; + tooltip?: string; buttonClass?: string; labelClass?: string; shortcut?: string; @@ -19,6 +20,7 @@ interface MenuItemProps { const MenuItem: React.FC = ({ label, description, + tooltip, buttonClass, labelClass, shortcut, @@ -36,6 +38,7 @@ const MenuItem: React.FC = ({ disabled && 'btn-disabled text-gray-400', buttonClass, )} + data-tip={tooltip ? tooltip : ''} onClick={onClick} disabled={disabled} > diff --git a/apps/readest-app/src/services/appService.ts b/apps/readest-app/src/services/appService.ts index 8b0a7ec9..b536ab59 100644 --- a/apps/readest-app/src/services/appService.ts +++ b/apps/readest-app/src/services/appService.ts @@ -2,7 +2,7 @@ import { AppPlatform, AppService, OsPlatform } from '@/types/system'; import { SystemSettings } from '@/types/settings'; import { FileSystem, BaseDir } from '@/types/system'; -import { Book, BookConfig, BookContent, BookFormat } from '@/types/book'; +import { Book, BookConfig, BookContent, BookFormat, ViewSettings } from '@/types/book'; import { getDir, getLocalBookFilename, @@ -74,6 +74,20 @@ export abstract class BaseAppService implements AppService { abstract selectDirectory(): Promise; abstract selectFiles(name: string, extensions: string[]): Promise; + getDefaultViewSettings(): ViewSettings { + return { + ...DEFAULT_BOOK_LAYOUT, + ...DEFAULT_BOOK_STYLE, + ...DEFAULT_BOOK_FONT, + ...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}), + ...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}), + ...DEFAULT_VIEW_CONFIG, + ...DEFAULT_TTS_CONFIG, + ...DEFAULT_SCREEN_CONFIG, + ...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() }, + }; + } + async loadSettings(): Promise { let settings: SystemSettings; const { fp, base } = this.resolvePath('settings.json', 'Settings'); @@ -90,15 +104,7 @@ export abstract class BaseAppService implements AppService { settings = { ...DEFAULT_SYSTEM_SETTINGS, ...settings }; settings.globalReadSettings = { ...DEFAULT_READSETTINGS, ...settings.globalReadSettings }; settings.globalViewSettings = { - ...DEFAULT_BOOK_LAYOUT, - ...DEFAULT_BOOK_STYLE, - ...DEFAULT_BOOK_FONT, - ...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}), - ...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}), - ...DEFAULT_VIEW_CONFIG, - ...DEFAULT_TTS_CONFIG, - ...DEFAULT_SCREEN_CONFIG, - ...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() }, + ...this.getDefaultViewSettings(), ...settings.globalViewSettings, }; } catch { @@ -110,17 +116,7 @@ export abstract class BaseAppService implements AppService { ...DEFAULT_READSETTINGS, ...(this.isMobile ? DEFAULT_MOBILE_READSETTINGS : {}), }, - globalViewSettings: { - ...DEFAULT_BOOK_LAYOUT, - ...DEFAULT_BOOK_STYLE, - ...DEFAULT_BOOK_FONT, - ...(this.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}), - ...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}), - ...DEFAULT_VIEW_CONFIG, - ...DEFAULT_TTS_CONFIG, - ...DEFAULT_SCREEN_CONFIG, - ...{ ...DEFAULT_TRANSLATOR_CONFIG, translateTargetLang: getTargetLang() }, - }, + globalViewSettings: this.getDefaultViewSettings(), } as SystemSettings; await this.fs.createDir('', 'Books', true); diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index f50673c1..1f1eadbb 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -2,7 +2,6 @@ export type BookFormat = 'EPUB' | 'PDF' | 'MOBI' | 'CBZ' | 'FB2' | 'FBZ'; export type BookNoteType = 'bookmark' | 'annotation' | 'excerpt'; export type HighlightStyle = 'highlight' | 'underline' | 'squiggly'; export type HighlightColor = 'red' | 'yellow' | 'green' | 'blue' | 'violet'; -import { CodeLanguage } from '@/utils/highlightjs'; export interface Book { // if Book is a remote book we just lazy load the book content via url @@ -115,7 +114,7 @@ export interface BookStyle { overrideLayout: boolean; overrideColor: boolean; codeHighlighting: boolean; - codeLanguage: CodeLanguage; + codeLanguage: string; userStylesheet: string; userUIStylesheet: string; } diff --git a/apps/readest-app/src/types/system.ts b/apps/readest-app/src/types/system.ts index fb0591df..0a679050 100644 --- a/apps/readest-app/src/types/system.ts +++ b/apps/readest-app/src/types/system.ts @@ -1,5 +1,5 @@ import { SystemSettings } from './settings'; -import { Book, BookConfig, BookContent } from './book'; +import { Book, BookConfig, BookContent, ViewSettings } from './book'; import { BookDoc } from '@/libs/document'; import { ProgressHandler } from '@/utils/transfer'; @@ -44,6 +44,7 @@ export interface AppService { selectDirectory(): Promise; selectFiles(name: string, extensions: string[]): Promise; + getDefaultViewSettings(): ViewSettings; loadSettings(): Promise; saveSettings(settings: SystemSettings): Promise; importBook(