feat: support global settings from library menu, closes #2140 (#2151)

This commit is contained in:
Huang Xin
2025-10-01 15:59:37 +08:00
committed by GitHub
parent d8943fc0c5
commit bdef593cdd
25 changed files with 129 additions and 119 deletions
@@ -1,10 +1,9 @@
import clsx from 'clsx';
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
import { PiUserCircle } from 'react-icons/pi';
import { PiUserCircleCheck } from 'react-icons/pi';
import { PiUserCircle, PiUserCircleCheck, PiGear } from 'react-icons/pi';
import { PiSun, PiMoon } from 'react-icons/pi';
import { TbSunMoon } from 'react-icons/tb';
import { BiMoon, BiSun } from 'react-icons/bi';
import { invoke, PermissionState } from '@tauri-apps/api/core';
import { isTauriAppPlatform, isWebAppPlatform } from '@/services/environment';
@@ -42,6 +41,7 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
const { userPlan, quotas } = useQuotaStats(true);
const { themeMode, setThemeMode } = useThemeStore();
const { settings, setSettings, saveSettings } = useSettingsStore();
const { setFontLayoutSettingsDialogOpen } = useSettingsStore();
const [isAutoUpload, setIsAutoUpload] = useState(settings.autoUpload);
const [isAutoCheckUpdates, setIsAutoCheckUpdates] = useState(settings.autoCheckUpdates);
const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(settings.alwaysOnTop);
@@ -174,6 +174,11 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
setIsDropdownOpen?.(false);
};
const openSettingsDialog = () => {
setIsDropdownOpen?.(false);
setFontLayoutSettingsDialogOpen(true);
};
const toggleAlwaysInForeground = async () => {
const requestAlwaysInForeground = !settings.alwaysInForeground;
@@ -297,9 +302,10 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
<MenuItem label={_('Reload Page')} onClick={handleReloadPage} />
<MenuItem
label={themeModeLabel}
Icon={themeMode === 'dark' ? BiMoon : themeMode === 'light' ? BiSun : TbSunMoon}
Icon={themeMode === 'dark' ? PiMoon : themeMode === 'light' ? PiSun : TbSunMoon}
onClick={cycleThemeMode}
/>
<MenuItem label={_('Settings')} Icon={PiGear} onClick={openSettingsDialog} />
{appService?.canCustomizeRootDir && (
<>
<hr aria-hidden='true' className='border-base-200 my-1' />
@@ -54,6 +54,7 @@ import LibraryHeader from './components/LibraryHeader';
import Bookshelf from './components/Bookshelf';
import useShortcuts from '@/hooks/useShortcuts';
import DropIndicator from '@/components/DropIndicator';
import SettingsDialog from '@/components/settings/SettingsDialog';
const LibraryPageWithSearchParams = () => {
const searchParams = useSearchParams();
@@ -77,6 +78,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
const { selectFiles } = useFileSelector(appService, _);
const { safeAreaInsets: insets, isRoundedWindow } = useThemeStore();
const { settings, setSettings, saveSettings } = useSettingsStore();
const { isFontLayoutSettingsDialogOpen } = useSettingsStore();
const [loading, setLoading] = useState(false);
const isInitiating = useRef(false);
const [libraryLoaded, setLibraryLoaded] = useState(false);
@@ -89,6 +91,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
}>({});
const [pendingNavigationBookIds, setPendingNavigationBookIds] = useState<string[] | null>(null);
const [isDragging, setIsDragging] = useState(false);
const demoBooks = useDemoBooks();
const osRef = useRef<OverlayScrollbarsComponentRef>(null);
const containerRef: React.MutableRefObject<HTMLDivElement | null> = useRef(null);
@@ -730,6 +733,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
<AboutWindow />
<UpdaterWindow />
<MigrateDataWindow />
{isFontLayoutSettingsDialogOpen && <SettingsDialog bookKey={''} />}
<Toast />
</div>
);
@@ -10,13 +10,13 @@ import { useBookDataStore } from '@/store/bookDataStore';
import { useTranslation } from '@/hooks/useTranslation';
import { getGridTemplate, getInsetEdges } from '@/utils/grid';
import { getViewInsets } from '@/utils/insets';
import SettingsDialog from '@/components/settings/SettingsDialog';
import FoliateViewer from './FoliateViewer';
import SectionInfo from './SectionInfo';
import HeaderBar from './HeaderBar';
import FooterBar from './FooterBar';
import ProgressInfoView from './ProgressInfo';
import Ribbon from './Ribbon';
import SettingsDialog from './settings/SettingsDialog';
import Annotator from './annotator/Annotator';
import FootnotePopup from './FootnotePopup';
import HintInfo from './HintInfo';
@@ -34,7 +34,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
const { getProgress, getViewState, getViewSettings } = useReaderStore();
const { setGridInsets, hoveredBookKey } = useReaderStore();
const { sideBarBookKey } = useSidebarStore();
const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useSettingsStore();
const { isFontLayoutSettingsDialogOpen } = useSettingsStore();
const { safeAreaInsets: screenInsets } = useThemeStore();
const aspectRatio = window.innerWidth / window.innerHeight;
@@ -120,7 +120,6 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
isTopLeft={index === 0}
isHoveredAnim={bookKeys.length > 2}
onCloseBook={onCloseBook}
onSetSettingsDialogOpen={setFontLayoutSettingsDialogOpen}
gridInsets={gridInsets}
/>
<FoliateViewer
@@ -203,7 +202,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
isHoveredAnim={false}
gridInsets={gridInsets}
/>
{isFontLayoutSettingsDialogOpen && <SettingsDialog bookKey={bookKey} config={config} />}
{isFontLayoutSettingsDialogOpen && <SettingsDialog bookKey={bookKey} />}
</div>
);
})}
@@ -18,9 +18,9 @@ import { useSidebarStore } from '@/store/sidebarStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { saveViewSettings } from '@/helpers/viewSettings';
import { eventDispatcher } from '@/utils/event';
import { viewPagination } from '../hooks/usePagination';
import { saveViewSettings } from '../utils/viewSettingsHelper';
import { PageInfo } from '@/types/book';
import { Insets } from '@/types/misc';
import Button from '@/components/Button';
@@ -26,7 +26,6 @@ interface HeaderBarProps {
isHoveredAnim: boolean;
gridInsets: Insets;
onCloseBook: (bookKey: string) => void;
onSetSettingsDialogOpen: (open: boolean) => void;
}
const HeaderBar: React.FC<HeaderBarProps> = ({
@@ -36,7 +35,6 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
isHoveredAnim,
gridInsets,
onCloseBook,
onSetSettingsDialogOpen,
}) => {
const _ = useTranslation();
const { appService } = useEnv();
@@ -185,7 +183,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
toggleButton={<PiDotsThreeVerticalBold size={iconSize16} />}
onToggle={handleToggleDropdown}
>
<ViewMenu bookKey={bookKey} onSetSettingsDialogOpen={onSetSettingsDialogOpen} />
<ViewMenu bookKey={bookKey} />
</Dropdown>
<WindowButtons
@@ -5,7 +5,7 @@ import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useBookDataStore } from '@/store/bookDataStore';
import { saveViewSettings } from '../utils/viewSettingsHelper';
import { saveViewSettings } from '@/helpers/viewSettings';
import { isTranslationAvailable } from '@/services/translators/utils';
import Button from '@/components/Button';
@@ -16,32 +16,29 @@ import { useAuth } from '@/context/AuthContext';
import { useThemeStore } from '@/store/themeStore';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { getStyles } from '@/utils/style';
import { navigateToLogin } from '@/utils/nav';
import { eventDispatcher } from '@/utils/event';
import { getMaxInlineSize } from '@/utils/config';
import { saveViewSettings } from '@/helpers/viewSettings';
import { tauriHandleToggleFullScreen } from '@/utils/window';
import { saveViewSettings } from '../utils/viewSettingsHelper';
import MenuItem from '@/components/MenuItem';
import Menu from '@/components/Menu';
interface ViewMenuProps {
bookKey: string;
setIsDropdownOpen?: (open: boolean) => void;
onSetSettingsDialogOpen: (open: boolean) => void;
}
const ViewMenu: React.FC<ViewMenuProps> = ({
bookKey,
setIsDropdownOpen,
onSetSettingsDialogOpen,
}) => {
const ViewMenu: React.FC<ViewMenuProps> = ({ bookKey, setIsDropdownOpen }) => {
const _ = useTranslation();
const router = useRouter();
const { user } = useAuth();
const { envConfig, appService } = useEnv();
const { getConfig, getBookData } = useBookDataStore();
const { setFontLayoutSettingsDialogOpen } = useSettingsStore();
const { getView, getViewSettings, getViewState, setViewSettings } = useReaderStore();
const config = getConfig(bookKey)!;
const bookData = getBookData(bookKey)!;
@@ -65,7 +62,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
const openFontLayoutMenu = () => {
setIsDropdownOpen?.(false);
onSetSettingsDialogOpen(true);
setFontLayoutSettingsDialogOpen(true);
};
const cycleThemeMode = () => {
@@ -87,7 +87,7 @@ export const AboutWindow = () => {
boxClassName='sm:!w-[480px] sm:!max-w-screen-sm sm:h-auto'
>
{isOpen && (
<div className='about-content flex h-full flex-col items-center justify-center gap-4'>
<div className='about-content flex h-full flex-col items-center justify-center gap-4 pb-10 sm:pb-0'>
<div className='flex flex-1 flex-col items-center justify-end gap-2 px-8 py-2'>
<div className='mb-2 mt-6'>
<Image src='/icon.png' alt='App Logo' className='h-20 w-20' width={64} height={64} />
@@ -402,7 +402,7 @@ export const UpdaterContent = ({
</div>
<div className='text-base-content text-sm'>
<h3 className='mb-2 font-bold'>{_('Changelog')}</h3>
<div className='bg-base-300 mb-4 rounded-lg px-4 pb-2 pt-4'>
<div className='bg-base-200 mb-4 rounded-lg px-4 pb-2 pt-4'>
{changelogs.length > 0 ? (
changelogs.map((entry: Changelog) => (
<div key={entry.version} className='mb-4'>
@@ -18,8 +18,8 @@ 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 { useResetViewSettings } from '@/hooks/useResetSettings';
import { saveViewSettings } from '@/helpers/viewSettings';
import { CODE_LANGUAGES, CodeLanguage, manageSyntaxHighlighting } from '@/utils/highlightjs';
import { SettingsPanelPanelProp } from './SettingsDialog';
import Select from '@/components/Select';
@@ -32,7 +32,7 @@ const ColorPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const { envConfig } = useEnv();
const { settings, setSettings } = useSettingsStore();
const { getView, getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const [invertImgColorInDark, setInvertImgColorInDark] = useState(
viewSettings.invertImgColorInDark,
);
@@ -42,9 +42,9 @@ const ColorPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const [editTheme, setEditTheme] = useState<CustomTheme | null>(null);
const [customThemes, setCustomThemes] = useState<Theme[]>([]);
const [showCustomThemeEditor, setShowCustomThemeEditor] = useState(false);
const [overrideColor, setOverrideColor] = useState(viewSettings.overrideColor!);
const [codeHighlighting, setcodeHighlighting] = useState(viewSettings.codeHighlighting!);
const [codeLanguage, setCodeLanguage] = useState(viewSettings.codeLanguage!);
const [overrideColor, setOverrideColor] = useState(viewSettings.overrideColor);
const [codeHighlighting, setcodeHighlighting] = useState(viewSettings.codeHighlighting);
const [codeLanguage, setCodeLanguage] = useState(viewSettings.codeLanguage);
const resetToDefaults = useResetViewSettings();
@@ -4,11 +4,12 @@ import { useReaderStore } from '@/store/readerStore';
import { useDeviceControlStore } from '@/store/deviceStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useBookDataStore } from '@/store/bookDataStore';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { useSettingsStore } from '@/store/settingsStore';
import { useResetViewSettings } from '@/hooks/useResetSettings';
import { getStyles } from '@/utils/style';
import { saveAndReload } from '@/utils/reload';
import { getMaxInlineSize } from '@/utils/config';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { saveViewSettings } from '@/helpers/viewSettings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import NumberInput from './NumberInput';
@@ -17,21 +18,20 @@ const ControlPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRes
const { envConfig, appService } = useEnv();
const { getView, getViewSettings } = useReaderStore();
const { getBookData } = useBookDataStore();
const { settings } = useSettingsStore();
const { acquireVolumeKeyInterception, releaseVolumeKeyInterception } = useDeviceControlStore();
const bookData = getBookData(bookKey)!;
const viewSettings = getViewSettings(bookKey)!;
const bookData = getBookData(bookKey);
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const [isScrolledMode, setScrolledMode] = useState(viewSettings.scrolled!);
const [isContinuousScroll, setIsContinuousScroll] = useState(viewSettings.continuousScroll!);
const [scrollingOverlap, setScrollingOverlap] = useState(viewSettings.scrollingOverlap!);
const [volumeKeysToFlip, setVolumeKeysToFlip] = useState(viewSettings.volumeKeysToFlip!);
const [isDisableClick, setIsDisableClick] = useState(viewSettings.disableClick!);
const [swapClickArea, setSwapClickArea] = useState(viewSettings.swapClickArea!);
const [isDisableDoubleClick, setIsDisableDoubleClick] = useState(
viewSettings.disableDoubleClick!,
);
const [animated, setAnimated] = useState(viewSettings.animated!);
const [allowScript, setAllowScript] = useState(viewSettings.allowScript!);
const [isScrolledMode, setScrolledMode] = useState(viewSettings.scrolled);
const [isContinuousScroll, setIsContinuousScroll] = useState(viewSettings.continuousScroll);
const [scrollingOverlap, setScrollingOverlap] = useState(viewSettings.scrollingOverlap);
const [volumeKeysToFlip, setVolumeKeysToFlip] = useState(viewSettings.volumeKeysToFlip);
const [isDisableClick, setIsDisableClick] = useState(viewSettings.disableClick);
const [swapClickArea, setSwapClickArea] = useState(viewSettings.swapClickArea);
const [isDisableDoubleClick, setIsDisableDoubleClick] = useState(viewSettings.disableDoubleClick);
const [animated, setAnimated] = useState(viewSettings.animated);
const [allowScript, setAllowScript] = useState(viewSettings.allowScript);
const resetToDefaults = useResetViewSettings();
@@ -234,7 +234,7 @@ const ControlPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRes
type='checkbox'
className='toggle'
checked={allowScript}
disabled={bookData.book?.format !== 'EPUB'}
disabled={bookData?.book?.format !== 'EPUB'}
onChange={() => setAllowScript(!allowScript)}
/>
</div>
@@ -5,10 +5,11 @@ import { IoMdCloseCircleOutline } from 'react-icons/io';
import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useSettingsStore } from '@/store/settingsStore';
import { useCustomFontStore } from '@/store/customFontStore';
import { useFileSelector } from '@/hooks/useFileSelector';
import { saveViewSettings } from '@/helpers/viewSettings';
import { CustomFont, mountCustomFont } from '@/styles/fonts';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
interface CustomFontsProps {
bookKey: string;
@@ -23,6 +24,7 @@ type FontFamily = {
const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
const _ = useTranslation();
const { appService, envConfig } = useEnv();
const { settings } = useSettingsStore();
const {
fonts: customFonts,
addFont,
@@ -32,7 +34,7 @@ const CustomFonts: React.FC<CustomFontsProps> = ({ bookKey, onBack }) => {
saveCustomFonts,
} = useCustomFontStore();
const { getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const [isDeleteMode, setIsDeleteMode] = useState(false);
const { selectFiles } = useFileSelector(appService, _);
@@ -27,8 +27,8 @@ import { getOSPlatform, isCJKEnv } from '@/utils/misc';
import { getSysFontsList } from '@/utils/bridge';
import { isCJKStr } from '@/utils/lang';
import { isTauriAppPlatform } from '@/services/environment';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { useResetViewSettings } from '@/hooks/useResetSettings';
import { saveViewSettings } from '@/helpers/viewSettings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import NumberInput from './NumberInput';
import FontDropdown from './FontDropDown';
@@ -91,7 +91,7 @@ const FontPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const _ = useTranslation();
const { envConfig, appService } = useEnv();
const { getView, getViewSettings } = useReaderStore();
const { fontPanelView, setFontPanelView } = useSettingsStore();
const { settings, fontPanelView, setFontPanelView } = useSettingsStore();
const {
fonts: allCustomFonts,
getAllFonts,
@@ -99,8 +99,8 @@ const FontPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
removeFont,
saveCustomFonts,
} = useCustomFontStore();
const viewSettings = getViewSettings(bookKey)!;
const view = getView(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const view = getView(bookKey);
const iconSize18 = useResponsiveSize(18);
const fontFamilyOptions = [
@@ -136,15 +136,15 @@ const FontPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
break;
}
const [sysFonts, setSysFonts] = useState<string[]>(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 [defaultCJKFont, setDefaultCJKFont] = useState(viewSettings.defaultCJKFont!);
const [serifFont, setSerifFont] = useState(viewSettings.serifFont!);
const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont!);
const [monospaceFont, setMonospaceFont] = useState(viewSettings.monospaceFont!);
const [fontWeight, setFontWeight] = useState(viewSettings.fontWeight!);
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 [defaultCJKFont, setDefaultCJKFont] = useState(viewSettings.defaultCJKFont);
const [serifFont, setSerifFont] = useState(viewSettings.serifFont);
const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont);
const [monospaceFont, setMonospaceFont] = useState(viewSettings.monospaceFont);
const [fontWeight, setFontWeight] = useState(viewSettings.fontWeight);
const [customFonts, setCustomFonts] = useState<string[]>(getFontFamilies());
const [CJKFonts, setCJKFonts] = useState<string[]>(() => {
@@ -5,11 +5,11 @@ import { useAuth } from '@/context/AuthContext';
import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useSettingsStore } from '@/store/settingsStore';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { saveViewSettings } from '@/helpers/viewSettings';
import { getTranslators } from '@/services/translators';
import { useResetViewSettings } from '@/hooks/useResetSettings';
import { TRANSLATED_LANGS, TRANSLATOR_LANGS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { saveAndReload } from '@/utils/reload';
import Select from '@/components/Select';
@@ -17,15 +17,15 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const _ = useTranslation();
const { token } = useAuth();
const { envConfig } = useEnv();
const { applyUILanguage } = useSettingsStore();
const { settings, applyUILanguage } = useSettingsStore();
const { getViewSettings, setViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
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 [showTranslateSource, setShowTranslateSource] = useState(viewSettings.showTranslateSource!);
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 [showTranslateSource, setShowTranslateSource] = useState(viewSettings.showTranslateSource);
const resetToDefaults = useResetViewSettings();
@@ -7,14 +7,15 @@ import { TbTextDirectionRtl } from 'react-icons/tb';
import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useSettingsStore } from '@/store/settingsStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResetViewSettings } from '../../hooks/useResetSettings';
import { useResetViewSettings } from '@/hooks/useResetSettings';
import { isCJKEnv } from '@/utils/misc';
import { getStyles } from '@/utils/style';
import { saveAndReload } from '@/utils/reload';
import { getMaxInlineSize } from '@/utils/config';
import { lockScreenOrientation } from '@/utils/bridge';
import { saveViewSettings } from '../../utils/viewSettingsHelper';
import { saveViewSettings } from '@/helpers/viewSettings';
import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book';
import { MIGHT_BE_RTL_LANGS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
@@ -24,52 +25,52 @@ import NumberInput from './NumberInput';
const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
const _ = useTranslation();
const { envConfig, appService } = useEnv();
const { settings } = useSettingsStore();
const { getView, getViewSettings, getGridInsets, setViewSettings } = useReaderStore();
const { getBookData } = useBookDataStore();
const view = getView(bookKey);
const bookData = getBookData(bookKey)!;
const viewSettings = getViewSettings(bookKey)!;
const gridInsets = getGridInsets(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin!);
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!);
const [wordSpacing, setWordSpacing] = useState(viewSettings.wordSpacing!);
const [letterSpacing, setLetterSpacing] = useState(viewSettings.letterSpacing!);
const view = getView(bookKey);
const bookData = getBookData(bookKey);
const gridInsets = getGridInsets(bookKey) || { top: 0, bottom: 0, left: 0, right: 0 };
const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin);
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight);
const [wordSpacing, setWordSpacing] = useState(viewSettings.wordSpacing);
const [letterSpacing, setLetterSpacing] = useState(viewSettings.letterSpacing);
const [textIndent, setTextIndent] = useState(viewSettings.textIndent!);
const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification!);
const [hyphenation, setHyphenation] = useState(viewSettings.hyphenation!);
const [marginTopPx, setMarginTopPx] = useState(
viewSettings.marginPx || viewSettings.marginTopPx!,
);
const [marginBottomPx, setMarginBottomPx] = useState(viewSettings.marginBottomPx!);
const [marginLeftPx, setMarginLeftPx] = useState(viewSettings.marginLeftPx!);
const [marginRightPx, setMarginRightPx] = useState(viewSettings.marginRightPx!);
const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification);
const [hyphenation, setHyphenation] = useState(viewSettings.hyphenation);
const [marginTopPx, setMarginTopPx] = useState(viewSettings.marginPx || viewSettings.marginTopPx);
const [marginBottomPx, setMarginBottomPx] = useState(viewSettings.marginBottomPx);
const [marginLeftPx, setMarginLeftPx] = useState(viewSettings.marginLeftPx);
const [marginRightPx, setMarginRightPx] = useState(viewSettings.marginRightPx);
const [compactMarginTopPx, setCompactMarginTopPx] = useState(
viewSettings.compactMarginPx || viewSettings.compactMarginTopPx!,
viewSettings.compactMarginPx || viewSettings.compactMarginTopPx,
);
const [compactMarginBottomPx, setCompactMarginBottomPx] = useState(
viewSettings.compactMarginBottomPx!,
viewSettings.compactMarginBottomPx,
);
const [gapPercent, setGapPercent] = useState(viewSettings.gapPercent!);
const [compactMarginLeftPx, setCompactMarginLeftPx] = useState(viewSettings.compactMarginLeftPx!);
const [gapPercent, setGapPercent] = useState(viewSettings.gapPercent);
const [compactMarginLeftPx, setCompactMarginLeftPx] = useState(viewSettings.compactMarginLeftPx);
const [compactMarginRightPx, setCompactMarginRightPx] = useState(
viewSettings.compactMarginRightPx!,
viewSettings.compactMarginRightPx,
);
const [maxColumnCount, setMaxColumnCount] = useState(viewSettings.maxColumnCount!);
const [maxInlineSize, setMaxInlineSize] = useState(viewSettings.maxInlineSize!);
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize!);
const [writingMode, setWritingMode] = useState(viewSettings.writingMode!);
const [overrideLayout, setOverrideLayout] = useState(viewSettings.overrideLayout!);
const [doubleBorder, setDoubleBorder] = useState(viewSettings.doubleBorder!);
const [borderColor, setBorderColor] = useState(viewSettings.borderColor!);
const [showHeader, setShowHeader] = useState(viewSettings.showHeader!);
const [showFooter, setShowFooter] = useState(viewSettings.showFooter!);
const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll!);
const [showRemainingTime, setShowRemainingTime] = useState(viewSettings.showRemainingTime!);
const [showRemainingPages, setShowRemainingPages] = useState(viewSettings.showRemainingPages!);
const [showProgressInfo, setShowProgressInfo] = useState(viewSettings.showProgressInfo!);
const [maxColumnCount, setMaxColumnCount] = useState(viewSettings.maxColumnCount);
const [maxInlineSize, setMaxInlineSize] = useState(viewSettings.maxInlineSize);
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize);
const [writingMode, setWritingMode] = useState(viewSettings.writingMode);
const [overrideLayout, setOverrideLayout] = useState(viewSettings.overrideLayout);
const [doubleBorder, setDoubleBorder] = useState(viewSettings.doubleBorder);
const [borderColor, setBorderColor] = useState(viewSettings.borderColor);
const [showHeader, setShowHeader] = useState(viewSettings.showHeader);
const [showFooter, setShowFooter] = useState(viewSettings.showFooter);
const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll);
const [showRemainingTime, setShowRemainingTime] = useState(viewSettings.showRemainingTime);
const [showRemainingPages, setShowRemainingPages] = useState(viewSettings.showRemainingPages);
const [showProgressInfo, setShowProgressInfo] = useState(viewSettings.showProgressInfo);
const [progressStyle, setProgressStyle] = useState(viewSettings.progressStyle);
const [screenOrientation, setScreenOrientation] = useState(viewSettings.screenOrientation!);
const [screenOrientation, setScreenOrientation] = useState(viewSettings.screenOrientation);
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
@@ -363,7 +364,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [screenOrientation]);
const langCode = getBookLangCode(bookData.bookDoc?.metadata?.language);
const langCode = getBookLangCode(bookData?.bookDoc?.metadata?.language);
const mightBeRTLBook = MIGHT_BE_RTL_LANGS.includes(langCode) || isCJKEnv();
const isVertical = viewSettings.vertical || writingMode.includes('vertical');
@@ -6,7 +6,7 @@ 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 { useResetViewSettings } from '@/hooks/useResetSettings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { getStyles } from '@/utils/style';
import cssValidate from '@/utils/css';
@@ -18,7 +18,7 @@ const MiscPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const { appService } = useEnv();
const { settings, isFontLayoutSettingsGlobal, setSettings } = useSettingsStore();
const { getView, getViewSettings, setViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
const [draftContentStylesheet, setDraftContentStylesheet] = useState(viewSettings.userStylesheet);
const [draftContentStylesheetSaved, setDraftContentStylesheetSaved] = useState(true);
@@ -1,6 +1,5 @@
import clsx from 'clsx';
import React, { useEffect, useRef, useState } from 'react';
import { BookConfig } from '@/types/book';
import { useEnv } from '@/context/EnvContext';
import { useSettingsStore } from '@/store/settingsStore';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
@@ -35,7 +34,7 @@ type TabConfig = {
label: string;
};
const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ bookKey }) => {
const SettingsDialog: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const _ = useTranslation();
const { appService } = useEnv();
const closeIconSize = useResponsiveSize(16);
@@ -168,7 +167,7 @@ const SettingsDialog: React.FC<{ bookKey: string; config: BookConfig }> = ({ boo
isOpen={true}
onClose={handleClose}
className='modal-open'
bgClassName='sm:!bg-black/20'
bgClassName={bookKey ? 'sm:!bg-black/20' : 'sm:!bg-black/50'}
boxClassName={clsx('sm:min-w-[520px]', appService?.isMobile && 'sm:max-w-[90%] sm:w-3/4')}
snapHeight={appService?.isMobile ? 0.7 : undefined}
header={
@@ -17,21 +17,24 @@ export const saveViewSettings = async <K extends keyof ViewSettings>(
useSettingsStore.getState();
const { getView, getViewSettings, setViewSettings } = useReaderStore.getState();
const { getConfig, saveConfig } = useBookDataStore.getState();
const viewSettings = getViewSettings(bookKey)!;
const config = getConfig(bookKey)!;
if (viewSettings[key] !== value) {
const viewSettings = getViewSettings(bookKey);
const config = getConfig(bookKey);
if (bookKey && viewSettings && viewSettings[key] !== value) {
viewSettings[key] = value;
if (applyStyles) {
const view = getView(bookKey);
view?.renderer.setStyles?.(getStyles(viewSettings));
}
}
setViewSettings(bookKey, viewSettings);
if (isFontLayoutSettingsGlobal && !skipGlobal) {
settings.globalViewSettings[key] = value;
setSettings(settings);
await saveSettings(envConfig, settings);
}
if (bookKey && config && viewSettings) {
setViewSettings(bookKey, viewSettings);
await saveConfig(envConfig, bookKey, config, settings);
}
await saveConfig(envConfig, bookKey, config, settings);
await saveSettings(envConfig, settings);
};
@@ -222,6 +222,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
},
getViewSettings: (key: string) => get().viewStates[key]?.viewSettings || null,
setViewSettings: (key: string, viewSettings: ViewSettings) => {
if (!key) return;
const id = key.split('-')[0]!;
const bookData = useBookDataStore.getState().booksData[id];
const viewState = get().viewStates[key];