135 lines
3.4 KiB
TypeScript
135 lines
3.4 KiB
TypeScript
import { get, set } from 'idb-keyval';
|
|
|
|
import type { GlobalState } from './GlobalStateStore.types';
|
|
|
|
const STORAGE_KEY = 'APP_GLO';
|
|
|
|
const SKIP_PATHS = new Set([
|
|
'app.controls',
|
|
'initialized',
|
|
'session.connected',
|
|
'session.error',
|
|
'session.loading',
|
|
]);
|
|
|
|
const shouldSkipPath = (path: string): boolean => {
|
|
return SKIP_PATHS.has(path);
|
|
};
|
|
|
|
const filterState = (state: unknown, prefix = ''): unknown => {
|
|
if (typeof state === 'function') {
|
|
return undefined;
|
|
}
|
|
|
|
if (state === null || typeof state !== 'object') {
|
|
return state;
|
|
}
|
|
|
|
if (Array.isArray(state)) {
|
|
return state.map((item, idx) => filterState(item, `${prefix}[${idx}]`));
|
|
}
|
|
|
|
const filtered: Record<string, unknown> = {};
|
|
for (const [key, value] of Object.entries(state)) {
|
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
|
|
if (shouldSkipPath(path) || typeof value === 'function') {
|
|
continue;
|
|
}
|
|
|
|
filtered[key] = filterState(value, path);
|
|
}
|
|
|
|
return filtered;
|
|
};
|
|
|
|
async function loadStorage(): Promise<Partial<GlobalState>> {
|
|
const result: Partial<GlobalState> = {};
|
|
const keys: (keyof GlobalState)[] = ['layout', 'navigation', 'owner', 'program', 'session', 'user'];
|
|
|
|
for (const key of keys) {
|
|
const storageKey = `${STORAGE_KEY}:${key}`;
|
|
|
|
// Always use localStorage for session
|
|
if (key === 'session') {
|
|
try {
|
|
if (typeof localStorage !== 'undefined') {
|
|
const data = localStorage.getItem(storageKey);
|
|
if (data) {
|
|
result[key] = JSON.parse(data);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to load ${key} from localStorage:`, e);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
if (typeof indexedDB !== 'undefined') {
|
|
const data = await get(storageKey);
|
|
if (data) {
|
|
result[key] = JSON.parse(data);
|
|
continue;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to load ${key} from IndexedDB, falling back to localStorage:`, e);
|
|
}
|
|
|
|
try {
|
|
if (typeof localStorage !== 'undefined') {
|
|
const data = localStorage.getItem(storageKey);
|
|
if (data) {
|
|
result[key] = JSON.parse(data);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to load ${key} from localStorage:`, e);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async function saveStorage(state: GlobalState): Promise<void> {
|
|
const keys: (keyof GlobalState)[] = ['layout', 'navigation', 'owner', 'program', 'session', 'user'];
|
|
|
|
for (const key of keys) {
|
|
const storageKey = `${STORAGE_KEY}:${key}`;
|
|
const filtered = filterState(state[key], key);
|
|
const serialized = JSON.stringify(filtered);
|
|
|
|
// Always use localStorage for session
|
|
if (key === 'session') {
|
|
try {
|
|
if (typeof localStorage !== 'undefined') {
|
|
localStorage.setItem(storageKey, serialized);
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to save ${key} to localStorage:`, e);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
if (typeof indexedDB !== 'undefined') {
|
|
await set(storageKey, serialized);
|
|
continue;
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to save ${key} to IndexedDB, falling back to localStorage:`, e);
|
|
}
|
|
|
|
try {
|
|
if (typeof localStorage !== 'undefined') {
|
|
localStorage.setItem(storageKey, serialized);
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to save ${key} to localStorage:`, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
export { loadStorage, saveStorage };
|