mirror of
https://github.com/karakeep-app/karakeep.git
synced 2026-02-28 18:25:55 +01:00
* Add initial impl * fix some format inconsistencies, add indicator in user settings when local is out of sync * Fix sliders in user settings, unify constants and formatting * address CodeRabbit suggestions * add mobile implementation * address coderabbit nitpicks * fix responsiveness of the reader settings popover * Move more of the web UI strings to i18n * update translations for more coverage * remove duplicate logic/definitions * fix android font family * add shared reading setting hook between web and mobile * unify reader settings context for both web and mobile * remove unused export * address coderabbit suggestions * fix tests
156 lines
4.3 KiB
TypeScript
156 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useMemo,
|
|
useState,
|
|
} from "react";
|
|
|
|
import {
|
|
ReaderSettingsProvider as BaseReaderSettingsProvider,
|
|
useReaderSettingsContext,
|
|
} from "@karakeep/shared-react/hooks/reader-settings";
|
|
import {
|
|
ReaderSettings,
|
|
ReaderSettingsPartial,
|
|
} from "@karakeep/shared/types/readers";
|
|
|
|
const LOCAL_STORAGE_KEY = "karakeep-reader-settings";
|
|
|
|
function getLocalOverridesFromStorage(): ReaderSettingsPartial {
|
|
if (typeof window === "undefined") return {};
|
|
try {
|
|
const stored = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
return stored ? JSON.parse(stored) : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function saveLocalOverridesToStorage(overrides: ReaderSettingsPartial): void {
|
|
if (typeof window === "undefined") return;
|
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(overrides));
|
|
}
|
|
|
|
// Session overrides context - web-specific feature for live preview
|
|
interface SessionOverridesContextValue {
|
|
sessionOverrides: ReaderSettingsPartial;
|
|
setSessionOverrides: React.Dispatch<
|
|
React.SetStateAction<ReaderSettingsPartial>
|
|
>;
|
|
}
|
|
|
|
const SessionOverridesContext =
|
|
createContext<SessionOverridesContextValue | null>(null);
|
|
|
|
export function ReaderSettingsProvider({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const [sessionOverrides, setSessionOverrides] =
|
|
useState<ReaderSettingsPartial>({});
|
|
|
|
const sessionValue = useMemo(
|
|
() => ({
|
|
sessionOverrides,
|
|
setSessionOverrides,
|
|
}),
|
|
[sessionOverrides],
|
|
);
|
|
|
|
// Memoize callbacks to prevent unnecessary re-renders
|
|
const getLocalOverrides = useCallback(getLocalOverridesFromStorage, []);
|
|
const saveLocalOverrides = useCallback(saveLocalOverridesToStorage, []);
|
|
const onClearSessionOverrides = useCallback(() => {
|
|
setSessionOverrides({});
|
|
}, []);
|
|
|
|
return (
|
|
<BaseReaderSettingsProvider
|
|
getLocalOverrides={getLocalOverrides}
|
|
saveLocalOverrides={saveLocalOverrides}
|
|
sessionOverrides={sessionOverrides}
|
|
onClearSessionOverrides={onClearSessionOverrides}
|
|
>
|
|
<SessionOverridesContext.Provider value={sessionValue}>
|
|
{children}
|
|
</SessionOverridesContext.Provider>
|
|
</BaseReaderSettingsProvider>
|
|
);
|
|
}
|
|
|
|
export function useReaderSettings() {
|
|
const sessionContext = useContext(SessionOverridesContext);
|
|
if (!sessionContext) {
|
|
throw new Error(
|
|
"useReaderSettings must be used within a ReaderSettingsProvider",
|
|
);
|
|
}
|
|
|
|
const { sessionOverrides, setSessionOverrides } = sessionContext;
|
|
const baseSettings = useReaderSettingsContext();
|
|
|
|
// Update session override (live preview, not persisted)
|
|
const updateSession = useCallback(
|
|
(updates: ReaderSettingsPartial) => {
|
|
setSessionOverrides((prev) => ({ ...prev, ...updates }));
|
|
},
|
|
[setSessionOverrides],
|
|
);
|
|
|
|
// Clear all session overrides
|
|
const clearSession = useCallback(() => {
|
|
setSessionOverrides({});
|
|
}, [setSessionOverrides]);
|
|
|
|
// Save current settings to local storage (this device only)
|
|
const saveToDevice = useCallback(() => {
|
|
const newLocalOverrides = {
|
|
...baseSettings.localOverrides,
|
|
...sessionOverrides,
|
|
};
|
|
baseSettings.setLocalOverrides(newLocalOverrides);
|
|
saveLocalOverridesToStorage(newLocalOverrides);
|
|
setSessionOverrides({});
|
|
}, [baseSettings, sessionOverrides, setSessionOverrides]);
|
|
|
|
// Clear a single local override
|
|
const clearLocalOverride = useCallback(
|
|
(key: keyof ReaderSettings) => {
|
|
baseSettings.clearLocal(key);
|
|
},
|
|
[baseSettings],
|
|
);
|
|
|
|
// Check if there are unsaved session changes
|
|
const hasSessionChanges = Object.keys(sessionOverrides).length > 0;
|
|
|
|
return {
|
|
// Current effective settings (what should be displayed)
|
|
settings: baseSettings.settings,
|
|
|
|
// Raw values for UI indicators
|
|
serverSettings: baseSettings.serverDefaults,
|
|
localOverrides: baseSettings.localOverrides,
|
|
sessionOverrides,
|
|
|
|
// State indicators
|
|
hasSessionChanges,
|
|
hasLocalOverrides: baseSettings.hasLocalOverrides,
|
|
isSaving: baseSettings.isSaving,
|
|
|
|
// Actions
|
|
updateSession,
|
|
clearSession,
|
|
saveToDevice,
|
|
clearLocalOverrides: baseSettings.clearAllLocal,
|
|
clearLocalOverride,
|
|
saveToServer: baseSettings.saveAsDefault,
|
|
updateServerSetting: baseSettings.saveAsDefault,
|
|
clearServerDefaults: baseSettings.clearAllDefaults,
|
|
};
|
|
}
|