// Per-user settings with localStorage fallback // Feature Registration Pattern implementation import type { ReaderContext } from "./core/reader-context"; import { apiGet, apiPut } from "../api"; import { getToken } from "../storage"; const LOCALSTORAGE_KEY = "reader_settings_local"; export function init(context: ReaderContext): void { let currentSettings: ReaderSettings | null = null; context.events.on("reader:init", async () => { currentSettings = await loadSettings(); context.events.emit("settings:loaded", currentSettings); }); context.events.on( "settings:save", async (detail: { settings: Partial }) => { await saveSettings(detail.settings); currentSettings = await loadSettings(); context.events.emit("settings:changed", currentSettings); }, ); context.events.on( "settings:get", (detail: { key?: keyof ReaderSettings }) => { if (currentSettings) { const value = detail.key ? currentSettings[detail.key] : currentSettings; context.events.emit("settings:current", { value }); } }, ); context.events.on( "settings:set", async (detail: { key: keyof ReaderSettings; value: any }) => { await saveSettings({ [detail.key]: detail.value }); currentSettings = await loadSettings(); context.events.emit("settings:changed", currentSettings); }, ); context.events.on("settings:sync", async () => { await syncSettings(); currentSettings = await loadSettings(); context.events.emit("settings:synced", currentSettings); }); } async function loadSettings(): Promise { const token = getToken(); if (!token) { const local = localStorage.getItem(LOCALSTORAGE_KEY); return local ? JSON.parse(local) : getDefaultSettings(); } try { const response = await apiGet("/readers/settings"); const settings = await response.json(); localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(settings)); return settings; } catch (error) { const local = localStorage.getItem(LOCALSTORAGE_KEY); return local ? JSON.parse(local) : getDefaultSettings(); } } async function saveSettings(settings: Partial): Promise { const token = getToken(); const current = await loadSettings(); const updated = { ...current, ...settings }; if (!token) { localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(updated)); return; } try { await apiPut("/readers/settings", settings); localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(updated)); } catch (error) { localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(updated)); } } async function syncSettings(): Promise { const local = localStorage.getItem(LOCALSTORAGE_KEY); if (!local) return; const settings = JSON.parse(local); const token = getToken(); if (token) { try { await apiPut("/readers/settings", settings); } catch (error) { console.error("Failed to sync settings:", error); } } } export function getDefaultSettings(): ReaderSettings { return { chrome_behavior: "auto-hide", progress_mode: "pages", chrome_theme: "tokyo-night", reading_theme: "dark", reading_font: "literata", tap_zone_size: 30, auto_scroll: false, panel_zoom_enabled: true, font_size: 16, line_height: 1.6, margin_width: 20, double_page_spread: false, reading_direction: "ltr", hardware_acceleration: true, panel_layout: { toc: { side: "left", visible: true, collapsed: false, width_px: 320, order: 1, locked: false, last_valid_side: "left", }, settings: { side: "left", visible: false, collapsed: true, width_px: 380, order: 2, locked: false, last_valid_side: "left", }, navigator: { side: "right", visible: false, collapsed: true, width_px: 280, order: 3, locked: false, last_valid_side: "right", }, bookmarks: { side: "right", visible: false, collapsed: true, width_px: 280, order: 4, locked: false, last_valid_side: "right", }, mobile_nav_visible: true, }, } as ReaderSettings; }