fix: avoid view glitch when initialize view settings, closes #713 (#728)

This commit is contained in:
Huang Xin
2025-03-27 19:40:41 +08:00
committed by GitHub
parent 3e38896078
commit 10aace0026
7 changed files with 47 additions and 78 deletions
@@ -19,7 +19,6 @@ import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { eventDispatcher } from '@/utils/event';
import { saveViewSettings } from '../utils/viewSettingsHelper';
import { PageInfo } from '@/types/book';
import { getStyles } from '@/utils/style';
import Button from '@/components/Button';
import Slider from '@/components/Slider';
@@ -59,14 +58,13 @@ const FooterBar: React.FC<FooterBarProps> = ({
const handleFontSizeChange = (value: number) => {
saveViewSettings(envConfig, bookKey, 'defaultFontSize', value);
view?.renderer.setStyles?.(getStyles(viewSettings!));
};
const handleMarginChange = (value: number) => {
const marginPx = Math.round((value / 100) * 88);
const gapPercent = Math.round((value / 100) * 10);
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx);
saveViewSettings(envConfig, bookKey, 'gapPercent', gapPercent);
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx, false, false);
saveViewSettings(envConfig, bookKey, 'gapPercent', gapPercent, false, false);
view?.renderer.setAttribute('margin', `${marginPx}px`);
view?.renderer.setAttribute('gap', `${gapPercent}%`);
if (viewSettings?.scrolled) {
@@ -76,7 +74,6 @@ const FooterBar: React.FC<FooterBarProps> = ({
const handleLineHeightChange = (value: number) => {
saveViewSettings(envConfig, bookKey, 'lineHeight', value / 10);
view?.renderer.setStyles?.(getStyles(viewSettings!));
};
const handleGoPrev = () => {
@@ -14,6 +14,7 @@ import { ThemeMode } from '@/styles/themes';
import { getStyles } from '@/utils/style';
import { getMaxInlineSize } from '@/utils/config';
import { tauriHandleToggleFullScreen } from '@/utils/window';
import { saveViewSettings } from '../utils/viewSettingsHelper';
import MenuItem from '@/components/MenuItem';
interface ViewMenuProps {
@@ -28,11 +29,11 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
onSetSettingsDialogOpen,
}) => {
const _ = useTranslation();
const { appService } = useEnv();
const { getView, getViews, getViewSettings, setViewSettings } = useReaderStore();
const { envConfig, appService } = useEnv();
const { getView, getViewSettings, setViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const { themeMode, themeCode, setThemeMode } = useThemeStore();
const { themeMode, setThemeMode } = useThemeStore();
const [isScrolledMode, setScrolledMode] = useState(viewSettings!.scrolled);
const [zoomLevel, setZoomLevel] = useState(viewSettings!.zoomLevel!);
@@ -58,13 +59,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
};
useEffect(() => {
getViews().forEach((view) => {
view.renderer.setStyles?.(getStyles(viewSettings!));
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [themeCode]);
useEffect(() => {
if (isScrolledMode === viewSettings!.scrolled) return;
viewSettings!.scrolled = isScrolledMode;
getView(bookKey)?.renderer.setAttribute('flow', isScrolledMode ? 'scrolled' : 'paginated');
getView(bookKey)?.renderer.setAttribute(
@@ -77,11 +72,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
}, [isScrolledMode]);
useEffect(() => {
const view = getView(bookKey);
if (!view) return;
viewSettings!.zoomLevel = zoomLevel;
setViewSettings(bookKey, viewSettings!);
view.renderer.setStyles?.(getStyles(viewSettings!));
saveViewSettings(envConfig, bookKey, 'zoomLevel', zoomLevel, true, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [zoomLevel]);
@@ -12,43 +12,25 @@ import {
Theme,
themes,
} from '@/styles/themes';
import { getStyles } from '@/utils/style';
import { useEnv } from '@/context/EnvContext';
import { useSettingsStore } from '@/store/settingsStore';
import { useThemeStore } from '@/store/themeStore';
import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import ThemeEditor from './ThemeEditor';
const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const ColorPanel: React.FC<{ bookKey: string }> = ({}) => {
const _ = useTranslation();
const {
themeMode,
themeColor,
themeCode,
isDarkMode,
setThemeMode,
setThemeColor,
saveCustomTheme,
} = useThemeStore();
const { themeMode, themeColor, isDarkMode, setThemeMode, setThemeColor, saveCustomTheme } =
useThemeStore();
const { envConfig } = useEnv();
const { settings, setSettings } = useSettingsStore();
const { getViews, getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const iconSize16 = useResponsiveSize(16);
const iconSize24 = useResponsiveSize(24);
const [editTheme, setEditTheme] = useState<CustomTheme | null>(null);
const [customThems, setCustomThemes] = useState<Theme[]>([]);
const [showCustomThemeEditor, setShowCustomThemeEditor] = useState(false);
useEffect(() => {
getViews().forEach((view) => {
view.renderer.setStyles?.(getStyles(viewSettings));
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [themeCode]);
useEffect(() => {
const customThemes = settings.globalReadSettings.customThemes ?? [];
setCustomThemes(
@@ -14,7 +14,6 @@ import {
import { useReaderStore } from '@/store/readerStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useEnv } from '@/context/EnvContext';
import { getStyles } from '@/utils/style';
import { getOSPlatform } from '@/utils/misc';
import { getSysFontsList } from '@/utils/font';
import { isTauriAppPlatform } from '@/services/environment';
@@ -64,8 +63,7 @@ const FontFace = ({
const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
const _ = useTranslation();
const { envConfig, appService } = useEnv();
const { getView, getViewSettings } = useReaderStore();
const view = getView(bookKey);
const { getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
const fontFamilyOptions = [
@@ -121,49 +119,41 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'defaultFont', defaultFont);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultFont]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'defaultFontSize', defaultFontSize);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultFontSize]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'minimumFontSize', minFontSize);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [minFontSize]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'fontWeight', fontWeight);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fontWeight]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'serifFont', serifFont);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [serifFont]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'sansSerifFont', sansSerifFont);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sansSerifFont]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'monospaceFont', monospaceFont);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [monospaceFont]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'overrideFont', overrideFont);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [overrideFont]);
@@ -46,54 +46,49 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'paragraphMargin', paragraphMargin);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [paragraphMargin]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'lineHeight', lineHeight);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lineHeight]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'wordSpacing', wordSpacing);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wordSpacing]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'letterSpacing', letterSpacing);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [letterSpacing]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'textIndent', textIndent);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [textIndent]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'fullJustification', fullJustification);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fullJustification]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'hyphenation', hyphenation);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hyphenation]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx);
if (marginPx === viewSettings.marginPx) return;
saveViewSettings(envConfig, bookKey, 'marginPx', marginPx, false, false);
view?.renderer.setAttribute('margin', `${marginPx}px`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [marginPx]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'gapPercent', gapPercent);
if (gapPercent === viewSettings.gapPercent) return;
saveViewSettings(envConfig, bookKey, 'gapPercent', gapPercent, false, false);
view?.renderer.setAttribute('gap', `${gapPercent}%`);
if (viewSettings.scrolled) {
view?.renderer.setAttribute('flow', 'scrolled');
@@ -102,25 +97,29 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
}, [gapPercent]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'maxColumnCount', maxColumnCount);
if (maxColumnCount === viewSettings.maxColumnCount) return;
saveViewSettings(envConfig, bookKey, 'maxColumnCount', maxColumnCount, false, false);
view?.renderer.setAttribute('max-column-count', maxColumnCount);
view?.renderer.setAttribute('max-inline-size', `${getMaxInlineSize(viewSettings)}px`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [maxColumnCount]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'maxInlineSize', maxInlineSize);
if (maxInlineSize === viewSettings.maxInlineSize) return;
saveViewSettings(envConfig, bookKey, 'maxInlineSize', maxInlineSize, false, false);
view?.renderer.setAttribute('max-inline-size', `${getMaxInlineSize(viewSettings)}px`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [maxInlineSize]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'maxBlockSize', maxBlockSize);
if (maxBlockSize === viewSettings.maxBlockSize) return;
saveViewSettings(envConfig, bookKey, 'maxBlockSize', maxBlockSize, false, false);
view?.renderer.setAttribute('max-block-size', `${maxBlockSize}px`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [maxBlockSize]);
useEffect(() => {
if (writingMode === viewSettings.writingMode) return;
// global settings are not supported for writing mode
const prevWritingMode = viewSettings.writingMode;
if (!writingMode.includes('vertical')) {
@@ -143,11 +142,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'overrideLayout', overrideLayout);
view?.renderer.setStyles?.(getStyles(viewSettings));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [overrideLayout]);
useEffect(() => {
if (isScrolledMode === viewSettings.scrolled) return;
saveViewSettings(envConfig, bookKey, 'scrolled', isScrolledMode);
getView(bookKey)?.renderer.setAttribute('flow', isScrolledMode ? 'scrolled' : 'paginated');
getView(bookKey)?.renderer.setAttribute(
@@ -159,6 +158,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
}, [isScrolledMode]);
useEffect(() => {
if (doubleBorder === viewSettings.doubleBorder) return;
if (doubleBorder && viewSettings.vertical) {
viewSettings.gapPercent = Math.max(
viewSettings.gapPercent,
@@ -167,16 +167,17 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
setGapPercent(viewSettings.gapPercent);
setViewSettings(bookKey, viewSettings);
}
saveViewSettings(envConfig, bookKey, 'doubleBorder', doubleBorder);
saveViewSettings(envConfig, bookKey, 'doubleBorder', doubleBorder, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [doubleBorder]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'borderColor', borderColor);
saveViewSettings(envConfig, bookKey, 'borderColor', borderColor, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [borderColor]);
useEffect(() => {
if (showHeader === viewSettings.showHeader) return;
if (showHeader && !viewSettings.vertical) {
viewSettings.marginPx = Math.max(viewSettings.marginPx, 44);
setMarginPx(viewSettings.marginPx);
@@ -189,11 +190,12 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
setGapPercent(viewSettings.gapPercent);
setViewSettings(bookKey, viewSettings);
}
saveViewSettings(envConfig, bookKey, 'showHeader', showHeader);
saveViewSettings(envConfig, bookKey, 'showHeader', showHeader, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showHeader]);
useEffect(() => {
if (showFooter === viewSettings.showFooter) return;
if (showFooter && !viewSettings.vertical) {
viewSettings.marginPx = Math.max(viewSettings.marginPx, 44);
setMarginPx(viewSettings.marginPx);
@@ -206,7 +208,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
setGapPercent(viewSettings.gapPercent);
setViewSettings(bookKey, viewSettings);
}
saveViewSettings(envConfig, bookKey, 'showFooter', showFooter);
saveViewSettings(envConfig, bookKey, 'showFooter', showFooter, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showFooter]);
@@ -114,13 +114,12 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
};
const handleSelectUILang = (option: string) => {
viewSettings.uiLanguage = option;
saveViewSettings(envConfig, bookKey, 'uiLanguage', option);
saveViewSettings(envConfig, bookKey, 'uiLanguage', option, false, false);
i18n.changeLanguage(option ? option : navigator.language);
};
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'animated', animated);
saveViewSettings(envConfig, bookKey, 'animated', animated, false, false);
if (animated) {
getView(bookKey)?.renderer.setAttribute('animated', '');
} else {
@@ -130,17 +129,17 @@ const MiscPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
}, [animated]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'disableClick', isDisableClick);
saveViewSettings(envConfig, bookKey, 'disableClick', isDisableClick, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDisableClick]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'swapClickArea', swapClickArea);
saveViewSettings(envConfig, bookKey, 'swapClickArea', swapClickArea, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [swapClickArea]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'continuousScroll', isContinuousScroll);
saveViewSettings(envConfig, bookKey, 'continuousScroll', isContinuousScroll, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isContinuousScroll]);
@@ -3,21 +3,29 @@ import { EnvConfigType } from '@/services/environment';
import { useBookDataStore } from '@/store/bookDataStore';
import { useReaderStore } from '@/store/readerStore';
import { useSettingsStore } from '@/store/settingsStore';
import { getStyles } from '@/utils/style';
export const saveViewSettings = async <K extends keyof ViewSettings>(
envConfig: EnvConfigType,
bookKey: string,
key: K,
value: ViewSettings[K],
skipGlobal?: boolean,
skipGlobal = false,
applyStyles = true,
) => {
const { settings, isFontLayoutSettingsGlobal, setSettings, saveSettings } =
useSettingsStore.getState();
const { getViewSettings, setViewSettings } = useReaderStore.getState();
const { getView, getViewSettings, setViewSettings } = useReaderStore.getState();
const { getConfig, saveConfig } = useBookDataStore.getState();
const viewSettings = getViewSettings(bookKey)!;
const config = getConfig(bookKey)!;
viewSettings[key] = value;
if (viewSettings[key] !== value) {
viewSettings[key] = value;
if (applyStyles) {
const view = getView(bookKey);
view?.renderer.setStyles?.(getStyles(viewSettings));
}
}
setViewSettings(bookKey, viewSettings);
if (isFontLayoutSettingsGlobal && !skipGlobal) {