97 lines
2.5 KiB
TypeScript
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 });
|
|
},
|
|
})); |