/**
* User themes — imported theme files that override a built-in (same id) or add a
* new theme. Persisted through the storage layer under the `themes` namespace (a
* file in the user-settings folder locally; localStorage only online), keyed by
* id like the presets store. Mirrors gui/lib/customStore.js.
* @module gui/theme/userThemeStore
*/
import { useCallback, useEffect, useState } from "react";
import { getCached, setCached, isHydrated, onHydrated } from "../../storage/cache.js";
const THEMES_NS = "themes";
/** @returns {object} The stored user themes, keyed by id (or `{}`). */
function read() {
return getCached(THEMES_NS) || {};
}
function write(obj) {
setCached(THEMES_NS, obj);
}
/** @returns {object[]} The user themes as an array (insertion order). */
export function getUserThemes() {
return Object.values(read());
}
/** Save (add or overwrite by id) a user theme. @param {object} theme */
export function saveUserTheme(theme) {
write({
...read(),
[theme.id]: theme
});
}
/** Remove a user theme by id. @param {string} id */
export function removeUserTheme(id) {
const o = {
...read()
};
delete o[id];
write(o);
}
/**
* React hook: the user-theme list plus add/remove actions that persist and
* re-read so the list stays in sync.
* @returns {[object[], (t: object) => void, (id: string) => void]}
*/
export function useUserThemes() {
const [themes, setThemes] = useState(() => getUserThemes());
// Online prerender two-pass: the storage cache is empty on the first paint (see useSettings), so
// re-read the stored themes once it hydrates. Local is already hydrated at first render — a no-op.
useEffect(() => {
if (isHydrated()) return undefined;
return onHydrated(() => setThemes(getUserThemes()));
}, []);
const add = useCallback(theme => {
saveUserTheme(theme);
setThemes(getUserThemes());
}, []);
const remove = useCallback(id => {
removeUserTheme(id);
setThemes(getUserThemes());
}, []);
return [themes, add, remove];
}