forked from akai/readest
feat: add support for per-book background image settings in addition to global settings, also closes #2223 (#2225)
This commit is contained in:
@@ -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;
|
||||
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;
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -142,10 +142,7 @@ const ColorPanel: React.FC<SettingsPanelPanelProp> = ({ 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -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: '',
|
||||
|
||||
@@ -288,10 +288,9 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
const oldConfig = bookData.config;
|
||||
const newConfig = {
|
||||
...bookData.config,
|
||||
updatedAt: Date.now(),
|
||||
progress,
|
||||
location,
|
||||
};
|
||||
} as BookConfig;
|
||||
|
||||
useBookDataStore.setState((state) => ({
|
||||
booksData: {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user