Files
waseel/lib/settings.ts

97 lines
2.5 KiB
TypeScript

import * as SecureStore from "expo-secure-store";
import { create } from "zustand";
/**
* Theme, language, keep-awake, and the overlay-permission flag are the only
* non-secret app preferences. SecureStore is used (not AsyncStorage) because
* AsyncStorage isn't actually installed in this project and SecureStore already
* backs the session — one small JSON blob stays well under its 2 KB value cap.
*/
const SETTINGS_KEY = "waseel_settings";
export type ThemeMode = "light" | "dark" | "system";
export type Lang = "en" | "ar" | "fr";
type PersistedSettings = Pick<
SettingsState,
"mode" | "lang" | "keepAwake" | "overlayRequested"
>;
type SettingsState = {
/** False until hydrate() has merged SecureStore values into the store. */
hydrated: boolean;
mode: ThemeMode;
lang: Lang;
keepAwake: boolean;
/**
* Only records that we sent the user to the system screen — SYSTEM_ALERT_WINDOW
* can't be queried from JS, so this is not a granted/denied signal.
*/
overlayRequested: boolean;
setMode: (mode: ThemeMode) => void;
setLang: (lang: Lang) => void;
setKeepAwake: (value: boolean) => void;
setOverlayRequested: (value: boolean) => void;
hydrate: () => Promise<void>;
};
const write = async (state: SettingsState) => {
const blob: PersistedSettings = {
mode: state.mode,
lang: state.lang,
keepAwake: state.keepAwake,
overlayRequested: state.overlayRequested,
};
await SecureStore.setItemAsync(SETTINGS_KEY, JSON.stringify(blob));
};
export const useSettingsStore = create<SettingsState>((set, get) => ({
hydrated: false,
mode: "system",
lang: "en",
keepAwake: false,
overlayRequested: false,
setMode: (mode) => {
set({ mode });
void write(get());
},
setLang: (lang) => {
set({ lang });
void write(get());
},
setKeepAwake: (value) => {
set({ keepAwake: value });
void write(get());
},
setOverlayRequested: (value) => {
set({ overlayRequested: value });
void write(get());
},
hydrate: async () => {
try {
const stored = await SecureStore.getItemAsync(SETTINGS_KEY);
if (stored) {
const parsed = JSON.parse(stored) as Partial<PersistedSettings>;
set({
mode: parsed.mode ?? "system",
lang: parsed.lang ?? "en",
keepAwake: parsed.keepAwake ?? false,
overlayRequested: parsed.overlayRequested ?? false,
hydrated: true,
});
return;
}
} catch (error) {
console.warn("[SETTINGS hydrate]: ", error);
}
set({ hydrated: true });
},
}));