import { Platform } from "react-native"; import * as SecureStore from "expo-secure-store"; /** * Boolean preference storage shared across platforms. * * Web persists via `localStorage` so behaviour matches the native * SecureStore path (also persistent). The web tier is origin-isolated * but not encrypted at rest — don't use this for secrets, only for UX * preferences whose disclosure is acceptable. Security-sensitive policy * (e.g. enforced auto-lock) belongs server-side. */ const WEB_STORAGE_AVAILABLE = Platform.OS === "web" && typeof window !== "undefined" && !!window.localStorage; let warnedNoStorage = false; function warnNoStorage(action: "load" | "save"): void { if (warnedNoStorage) { return; } warnedNoStorage = true; if (!__DEV__) { return; } console.warn( `[settings-storage] ${action} failed: localStorage unavailable; using fallbacks for this session.` ); } export async function loadSetting(key: string, fallback: boolean): Promise { try { if (Platform.OS === "web") { if (!WEB_STORAGE_AVAILABLE) { warnNoStorage("load"); return fallback; } const v = window.localStorage.getItem(key); return v !== null ? v === "true" : fallback; } const v = await SecureStore.getItemAsync(key); return v !== null ? v === "true" : fallback; } catch (e) { if (__DEV__) { console.warn(`[settings-storage] load(${key}) threw:`, e); } return fallback; } } export async function saveSetting(key: string, value: boolean): Promise { try { if (Platform.OS === "web") { if (!WEB_STORAGE_AVAILABLE) { warnNoStorage("save"); return; } window.localStorage.setItem(key, String(value)); return; } await SecureStore.setItemAsync(key, String(value)); } catch (e) { if (__DEV__) { console.warn(`[settings-storage] save(${key}) threw:`, e); } } } /** * Same storage backend as load/saveSetting (SecureStore native, * localStorage web) but for arbitrary JSON-serializable values instead * of only booleans — e.g. an ordered list of ids, or a per-id map. * `fallback` covers both "never saved" and "saved value doesn't parse" * (a corrupt/edited-by-hand entry shouldn't crash the caller). */ export async function loadJsonSetting(key: string, fallback: T): Promise { try { const raw = Platform.OS === "web" ? WEB_STORAGE_AVAILABLE ? window.localStorage.getItem(key) : null : await SecureStore.getItemAsync(key); if (raw === null) { return fallback; } return JSON.parse(raw) as T; } catch (e) { if (__DEV__) { console.warn(`[settings-storage] loadJsonSetting(${key}) threw:`, e); } return fallback; } } export async function saveJsonSetting(key: string, value: T): Promise { try { const raw = JSON.stringify(value); if (Platform.OS === "web") { if (!WEB_STORAGE_AVAILABLE) { warnNoStorage("save"); return; } window.localStorage.setItem(key, raw); return; } await SecureStore.setItemAsync(key, raw); } catch (e) { if (__DEV__) { console.warn(`[settings-storage] saveJsonSetting(${key}) threw:`, e); } } }