feat(memoir): persist chapter reading prefs globally
Share font size, font family, and background across all memoir chapters via MemoirReadingSettingsProvider and SecureStore (same app-settings pattern). Add parse/merge helpers and unit tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,11 +10,18 @@ import { supportedLanguages, type AppLanguage } from '@/i18n/resources';
|
||||
|
||||
import { THEME_NAMES, type ThemeName } from '@/constants/theme-bridge';
|
||||
|
||||
import {
|
||||
mergeMemoirReadingPreferences,
|
||||
parseMemoirReadingPreferences,
|
||||
type MemoirReadingPreferences,
|
||||
} from './memoir-reading-settings';
|
||||
|
||||
const KEY_LANGUAGE = 'app_settings_language';
|
||||
const KEY_LARGE_TEXT = 'app_settings_large_text';
|
||||
const KEY_DARK_MODE = 'app_settings_dark_mode';
|
||||
const KEY_THEME_NAME = 'app_settings_theme_name';
|
||||
const KEY_TTS_SPEAK_DEFAULT = 'app_settings_tts_speak_default';
|
||||
const KEY_MEMOIR_READING = 'app_settings_memoir_reading';
|
||||
|
||||
const webFallback: Record<string, string> = {};
|
||||
|
||||
@@ -95,5 +102,20 @@ export async function setTtsSpeakDefault(value: boolean): Promise<void> {
|
||||
await setStored(KEY_TTS_SPEAK_DEFAULT, value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
export async function getMemoirReadingPreferences(): Promise<MemoirReadingPreferences> {
|
||||
const raw = await getStored(KEY_MEMOIR_READING);
|
||||
return parseMemoirReadingPreferences(raw);
|
||||
}
|
||||
|
||||
export async function setMemoirReadingPreferences(
|
||||
patch: Partial<MemoirReadingPreferences>,
|
||||
): Promise<void> {
|
||||
const raw = await getStored(KEY_MEMOIR_READING);
|
||||
const current = parseMemoirReadingPreferences(raw);
|
||||
const next = mergeMemoirReadingPreferences(current, patch);
|
||||
await setStored(KEY_MEMOIR_READING, JSON.stringify(next));
|
||||
}
|
||||
|
||||
export { supportedLanguages, THEME_NAMES };
|
||||
export type { AppLanguage, ThemeName };
|
||||
export type { MemoirReadingPreferences } from './memoir-reading-settings';
|
||||
|
||||
70
app-expo/src/core/settings/memoir-reading-settings.ts
Normal file
70
app-expo/src/core/settings/memoir-reading-settings.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
export type MemoirReadingFontSize = 'small' | 'default' | 'large';
|
||||
|
||||
export type MemoirReadingFontFamily = 'serif' | 'sans';
|
||||
|
||||
export type MemoirReadingBackground = 'white' | 'sepia';
|
||||
|
||||
export interface MemoirReadingPreferences {
|
||||
fontSize: MemoirReadingFontSize;
|
||||
fontFamily: MemoirReadingFontFamily;
|
||||
background: MemoirReadingBackground;
|
||||
}
|
||||
|
||||
export const DEFAULT_MEMOIR_READING_PREFERENCES: MemoirReadingPreferences = {
|
||||
fontSize: 'default',
|
||||
fontFamily: 'serif',
|
||||
background: 'white',
|
||||
};
|
||||
|
||||
const FONT_SIZES: MemoirReadingFontSize[] = ['small', 'default', 'large'];
|
||||
const FONT_FAMS: MemoirReadingFontFamily[] = ['serif', 'sans'];
|
||||
const BACKGROUNDS: MemoirReadingBackground[] = ['white', 'sepia'];
|
||||
|
||||
function isFontSize(v: unknown): v is MemoirReadingFontSize {
|
||||
return typeof v === 'string' && FONT_SIZES.includes(v as MemoirReadingFontSize);
|
||||
}
|
||||
|
||||
function isFontFamily(v: unknown): v is MemoirReadingFontFamily {
|
||||
return typeof v === 'string' && FONT_FAMS.includes(v as MemoirReadingFontFamily);
|
||||
}
|
||||
|
||||
function isBackground(v: unknown): v is MemoirReadingBackground {
|
||||
return typeof v === 'string' && BACKGROUNDS.includes(v as MemoirReadingBackground);
|
||||
}
|
||||
|
||||
export function parseMemoirReadingPreferences(
|
||||
raw: string | null,
|
||||
): MemoirReadingPreferences {
|
||||
if (raw == null || raw.trim() === '') {
|
||||
return { ...DEFAULT_MEMOIR_READING_PREFERENCES };
|
||||
}
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(raw);
|
||||
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||
return { ...DEFAULT_MEMOIR_READING_PREFERENCES };
|
||||
}
|
||||
const o = parsed as Record<string, unknown>;
|
||||
const d = DEFAULT_MEMOIR_READING_PREFERENCES;
|
||||
return {
|
||||
fontSize: isFontSize(o.fontSize) ? o.fontSize : d.fontSize,
|
||||
fontFamily: isFontFamily(o.fontFamily) ? o.fontFamily : d.fontFamily,
|
||||
background: isBackground(o.background) ? o.background : d.background,
|
||||
};
|
||||
} catch {
|
||||
return { ...DEFAULT_MEMOIR_READING_PREFERENCES };
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeMemoirReadingPreferences(
|
||||
base: MemoirReadingPreferences,
|
||||
patch: Partial<MemoirReadingPreferences>,
|
||||
): MemoirReadingPreferences {
|
||||
return {
|
||||
fontSize:
|
||||
patch.fontSize !== undefined ? patch.fontSize : base.fontSize,
|
||||
fontFamily:
|
||||
patch.fontFamily !== undefined ? patch.fontFamily : base.fontFamily,
|
||||
background:
|
||||
patch.background !== undefined ? patch.background : base.background,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user