Files
toolbox-portal/src/usePreferences.ts

66 lines
1.8 KiB
TypeScript

import { useEffect, useState } from 'react';
import {
PREFERENCES_KEY,
defaultPreferences,
parsePreferences,
readPreferences,
writePreferences,
type Preferences,
} from './preferences';
interface InitialPreferences {
preferences: Preferences;
storageAvailable: boolean;
}
function initializePreferences(): InitialPreferences {
try {
const preferences = readPreferences();
const probeKey = `${PREFERENCES_KEY}:probe`;
localStorage.setItem(probeKey, '1');
localStorage.removeItem(probeKey);
return { preferences, storageAvailable: true };
} catch {
return { preferences: { ...defaultPreferences }, storageAvailable: false };
}
}
export function usePreferences(): [
Preferences,
(next: Preferences | ((current: Preferences) => Preferences)) => void,
boolean,
] {
const [initial] = useState(initializePreferences);
const [preferences, setPreferences] = useState<Preferences>(
initial.preferences
);
useEffect(() => {
try {
writePreferences(preferences);
} catch {
// The UI already reports storage that was unavailable during initialization.
// A later quota/privacy-mode failure leaves this session's in-memory choices intact.
}
}, [preferences]);
useEffect(() => {
const handleStorage = (event: StorageEvent) => {
if (event.key !== PREFERENCES_KEY) return;
if (event.newValue === null) {
setPreferences({ ...defaultPreferences });
return;
}
try {
setPreferences(parsePreferences(JSON.parse(event.newValue)));
} catch {
setPreferences({ ...defaultPreferences });
}
};
globalThis.addEventListener('storage', handleStorage);
return () => globalThis.removeEventListener('storage', handleStorage);
}, []);
return [preferences, setPreferences, initial.storageAvailable];
}