66 lines
1.8 KiB
TypeScript
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];
|
|
}
|