diff --git a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx index 7bf454df..59989549 100644 --- a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx +++ b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx @@ -15,6 +15,7 @@ import { usePagination } from '../hooks/usePagination'; import { useFoliateEvents } from '../hooks/useFoliateEvents'; import { useProgressSync } from '../hooks/useProgressSync'; import { useProgressAutoSave } from '../hooks/useProgressAutoSave'; +import { useBackgroundTexture } from '@/hooks/useBackgroundTexture'; import { useAutoFocus } from '@/hooks/useAutoFocus'; import { useTranslation } from '@/hooks/useTranslation'; import { useKOSync } from '../hooks/useKOSync'; @@ -73,6 +74,7 @@ const FoliateViewer: React.FC<{ const { getViewState, getViewSettings, setViewSettings } = useReaderStore(); const { getParallels } = useParallelViewStore(); const { getBookData } = useBookDataStore(); + const { applyBackgroundTexture } = useBackgroundTexture(); const viewState = getViewState(bookKey); const viewSettings = getViewSettings(bookKey); @@ -145,14 +147,19 @@ const FoliateViewer: React.FC<{ const writingDir = viewRef.current?.renderer.setStyles && getDirection(detail.doc); const viewSettings = getViewSettings(bookKey)!; const bookData = getBookData(bookKey)!; - viewSettings.vertical = + + const newVertical = writingDir?.vertical || viewSettings.writingMode.includes('vertical') || false; - viewSettings.rtl = + const newRtl = writingDir?.rtl || getDirFromUILanguage() === 'rtl' || viewSettings.writingMode.includes('rl') || false; - setViewSettings(bookKey, { ...viewSettings }); + if (viewSettings.vertical !== newVertical || viewSettings.rtl !== newRtl) { + viewSettings.vertical = newVertical; + viewSettings.rtl = newRtl; + setViewSettings(bookKey, { ...viewSettings }); + } mountAdditionalFonts(detail.doc, isCJKLang(bookData.book?.primaryLanguage)); @@ -395,6 +402,16 @@ const FoliateViewer: React.FC<{ // eslint-disable-next-line react-hooks/exhaustive-deps }, [settings.customFonts, envConfig]); + useEffect(() => { + if (!viewSettings) return; + applyBackgroundTexture(envConfig, viewSettings); + }, [ + viewSettings?.backgroundTextureId, + viewSettings?.backgroundOpacity, + viewSettings?.backgroundSize, + applyBackgroundTexture, + ]); + useEffect(() => { if (viewRef.current && viewRef.current.renderer) { doubleClickDisabled.current = !!viewSettings?.disableDoubleClick; diff --git a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts index dee3a823..7de45f87 100644 --- a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts +++ b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts @@ -142,7 +142,11 @@ export const useProgressSync = (bookKey: string) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars Object.entries(syncedConfig).filter(([_, value]) => value !== null && value !== undefined), ); - setConfig(bookKey, { ...config, ...filteredSyncedConfig }); + if (syncedConfig.updatedAt >= config.updatedAt) { + setConfig(bookKey, { ...config, ...filteredSyncedConfig }); + } else { + setConfig(bookKey, { ...filteredSyncedConfig, ...config }); + } if (remoteCFILocation && configCFI) { if (CFI.compare(configCFI, remoteCFILocation) < 0) { if (view) { diff --git a/apps/readest-app/src/components/Providers.tsx b/apps/readest-app/src/components/Providers.tsx index eff1da3d..b2ae0cc2 100644 --- a/apps/readest-app/src/components/Providers.tsx +++ b/apps/readest-app/src/components/Providers.tsx @@ -12,14 +12,14 @@ import { useSettingsStore } from '@/store/settingsStore'; import { useDeviceControlStore } from '@/store/deviceStore'; import { useSafeAreaInsets } from '@/hooks/useSafeAreaInsets'; import { useDefaultIconSize } from '@/hooks/useResponsiveSize'; -import { useCustomTextureStore } from '@/store/customTextureStore'; +import { useBackgroundTexture } from '@/hooks/useBackgroundTexture'; import { getLocale } from '@/utils/misc'; const Providers = ({ children }: { children: React.ReactNode }) => { const { envConfig, appService } = useEnv(); const { applyUILanguage } = useSettingsStore(); const { setScreenBrightness } = useDeviceControlStore(); - const { addTexture, applyTexture } = useCustomTextureStore(); + const { applyBackgroundTexture } = useBackgroundTexture(); const iconSize = useDefaultIconSize(); useSafeAreaInsets(); // Initialize safe area insets @@ -47,21 +47,10 @@ const Providers = ({ children }: { children: React.ReactNode }) => { if (appService.hasScreenBrightness && brightness >= 0) { setScreenBrightness(brightness / 100); } - const textureId = globalViewSettings.backgroundTextureId; - const textureOpacity = globalViewSettings.backgroundOpacity; - const textureSize = globalViewSettings.backgroundSize; - if (textureId && textureId !== 'none') { - document.documentElement.style.setProperty('--bg-texture-opacity', `${textureOpacity}`); - document.documentElement.style.setProperty('--bg-texture-size', textureSize); - const customTexture = settings.customTextures?.find((t) => t.id === textureId); - if (customTexture) { - addTexture(customTexture.path); - } - applyTexture(envConfig, textureId); - } + applyBackgroundTexture(envConfig, globalViewSettings); }); } - }, [envConfig, appService, applyUILanguage, addTexture, applyTexture, setScreenBrightness]); + }, [envConfig, appService, applyUILanguage, setScreenBrightness, applyBackgroundTexture]); // Make sure appService is available in all children components if (!appService) return; diff --git a/apps/readest-app/src/components/settings/ColorPanel.tsx b/apps/readest-app/src/components/settings/ColorPanel.tsx index c2235842..59f6d108 100644 --- a/apps/readest-app/src/components/settings/ColorPanel.tsx +++ b/apps/readest-app/src/components/settings/ColorPanel.tsx @@ -142,10 +142,7 @@ const ColorPanel: React.FC = ({ bookKey, onRegisterReset const applyBackgroundTexture = () => { applyTexture(envConfig, selectedTextureId); - document.documentElement.style.setProperty( - '--bg-texture-opacity', - backgroundOpacity.toString(), - ); + document.documentElement.style.setProperty('--bg-texture-opacity', `${backgroundOpacity}`); document.documentElement.style.setProperty('--bg-texture-size', backgroundSize); }; diff --git a/apps/readest-app/src/hooks/useBackgroundTexture.ts b/apps/readest-app/src/hooks/useBackgroundTexture.ts new file mode 100644 index 00000000..85f1e3e2 --- /dev/null +++ b/apps/readest-app/src/hooks/useBackgroundTexture.ts @@ -0,0 +1,31 @@ +import { useCallback } from 'react'; +import { useCustomTextureStore } from '@/store/customTextureStore'; +import { useSettingsStore } from '@/store/settingsStore'; +import { EnvConfigType } from '@/services/environment'; +import { ViewSettings } from '@/types/book'; + +export const useBackgroundTexture = () => { + const applyBackgroundTexture = useCallback( + (envConfig: EnvConfigType, viewSettings: ViewSettings) => { + const textureId = viewSettings.backgroundTextureId; + const textureOpacity = viewSettings.backgroundOpacity; + const textureSize = viewSettings.backgroundSize; + if (!textureId || textureId === 'none') return; + + document.documentElement.style.setProperty('--bg-texture-opacity', `${textureOpacity}`); + document.documentElement.style.setProperty('--bg-texture-size', textureSize); + + const settings = useSettingsStore.getState().settings; + const customTexture = settings.customTextures?.find((t) => t.id === textureId); + + if (customTexture) { + useCustomTextureStore.getState().addTexture(customTexture.path); + } + + useCustomTextureStore.getState().applyTexture(envConfig, textureId); + }, + [], + ); + + return { applyBackgroundTexture }; +}; diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index 2cb76c10..e359d78b 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -155,7 +155,7 @@ export const DEFAULT_BOOK_STYLE: BookStyle = { overrideColor: false, backgroundTextureId: 'none', backgroundOpacity: 0.6, - backgroundSize: 'auto', + backgroundSize: 'cover', codeHighlighting: false, codeLanguage: 'auto-detect', userStylesheet: '', diff --git a/apps/readest-app/src/store/readerStore.ts b/apps/readest-app/src/store/readerStore.ts index ce1896f8..8f71910f 100644 --- a/apps/readest-app/src/store/readerStore.ts +++ b/apps/readest-app/src/store/readerStore.ts @@ -288,10 +288,9 @@ export const useReaderStore = create((set, get) => ({ const oldConfig = bookData.config; const newConfig = { ...bookData.config, - updatedAt: Date.now(), progress, location, - }; + } as BookConfig; useBookDataStore.setState((state) => ({ booksData: { diff --git a/apps/readest-app/src/styles/textures.ts b/apps/readest-app/src/styles/textures.ts index 05c213e6..6f234c93 100644 --- a/apps/readest-app/src/styles/textures.ts +++ b/apps/readest-app/src/styles/textures.ts @@ -59,7 +59,7 @@ const createTextureCSS = (texture: BackgroundTexture) => { body::before, .sidebar-container::before, .notebook-container::before, .foliate-viewer::before { content: ""; - position: fixed; + position: absolute; top: 0; left: 0; right: 0; @@ -68,10 +68,14 @@ const createTextureCSS = (texture: BackgroundTexture) => { z-index: 0; background-image: url("${texture.blobUrl || texture.url}"); background-repeat: repeat; - background-size: var(--bg-texture-size, auto); - mix-blend-mode: var(--bg-texture-blend-mode, normal); + background-size: var(--bg-texture-size, cover); + mix-blend-mode: var(--bg-texture-blend-mode, multiply); opacity: var(--bg-texture-opacity, 0.6); - }`; + } + body::before { + height: 100vh; + } + `; return css; };