Remote storage for storywriter
This commit is contained in:
parent
2871c19177
commit
ba3b500063
|
|
@ -0,0 +1,51 @@
|
|||
import { useEffect, useReducer, useState, type Dispatch, type Reducer, type StateUpdater } from "preact/hooks";
|
||||
import { loadObject, saveObject } from "@common/storage";
|
||||
|
||||
export const useRemoteState = <T>(key: string, initialValue: T) => {
|
||||
const storedKey = `useRemoteState.${key}`;
|
||||
|
||||
const [value, setValue] = useState<T>(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
loadObject<T>(storedKey, initialValue).then(setValue);
|
||||
}, [storedKey]);
|
||||
|
||||
useEffect(() => {
|
||||
saveObject(storedKey, value);
|
||||
}, [storedKey, value]);
|
||||
|
||||
return [value, setValue] as [T, Dispatch<StateUpdater<T>>];
|
||||
};
|
||||
|
||||
const hydrate = Symbol('hydrate');
|
||||
|
||||
type HydrateAction<T> = { type: typeof hydrate; payload: T };
|
||||
|
||||
const isHydrateAction = <T>(action: unknown): action is HydrateAction<T> =>
|
||||
typeof action === 'object' && action !== null && 'type' in action && action.type === hydrate;
|
||||
|
||||
const wrapReducer = <T, A>(reducer: Reducer<T, A>): Reducer<T, A | HydrateAction<T>> =>
|
||||
(state, action) => {
|
||||
if (isHydrateAction<T>(action)) {
|
||||
return action.payload;
|
||||
}
|
||||
return reducer(state, action);
|
||||
};
|
||||
|
||||
export const useRemoteReducer = <T, A>(key: string, reducer: Reducer<T, A>, initialValue: T) => {
|
||||
const storedKey = `useRemoteReducer.${key}`;
|
||||
|
||||
const [state, dispatch] = useReducer(wrapReducer(reducer), initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
loadObject<T>(storedKey, initialValue).then((loaded) => {
|
||||
dispatch({ type: hydrate, payload: loaded });
|
||||
});
|
||||
}, [storedKey]);
|
||||
|
||||
useEffect(() => {
|
||||
saveObject(storedKey, state);
|
||||
}, [storedKey, state]);
|
||||
|
||||
return [state, dispatch] as [T, Dispatch<A>];
|
||||
};
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
const API_KEY = 'awoorwa32';
|
||||
|
||||
const saveThrottleDelay = 2000;
|
||||
const pendingSaves = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
export const loadObject = async <T>(key: string, defaultObject: T): Promise<T> => {
|
||||
let localObject: Partial<T> = {};
|
||||
|
||||
|
|
@ -23,7 +26,16 @@ export const loadObject = async <T>(key: string, defaultObject: T): Promise<T> =
|
|||
return { ...defaultObject, ...localObject, ...remoteObject };
|
||||
}
|
||||
|
||||
export const saveObject = async <T>(key: string, obj: T) => {
|
||||
export const saveObject = <T>(key: string, obj: T) => {
|
||||
const existing = pendingSaves.get(key);
|
||||
if (existing) clearTimeout(existing);
|
||||
pendingSaves.set(key, setTimeout(() => {
|
||||
pendingSaves.delete(key);
|
||||
doSaveObject(key, obj);
|
||||
}, saveThrottleDelay));
|
||||
};
|
||||
|
||||
const doSaveObject = async <T>(key: string, obj: T) => {
|
||||
const saveData = JSON.stringify(obj);
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { createContext } from "preact";
|
||||
import { useContext, useMemo, useReducer } from "preact/hooks";
|
||||
import { useContext, useMemo } from "preact/hooks";
|
||||
|
||||
import LLM from "../utils/llm";
|
||||
import Chapters from "../utils/chapters";
|
||||
import { useStoredReducer } from "@common/hooks/useStoredState";
|
||||
import { useRemoteReducer } from "@common/hooks/useRemote";
|
||||
|
||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -561,7 +561,7 @@ export const useAppState = () => useContext(StateContext);
|
|||
// ─── Provider ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const StateContextProvider = ({ children }: { children?: any }) => {
|
||||
const [state, dispatch] = useStoredReducer('storywriter.state', reducer, DEFAULT_STATE);
|
||||
const [state, dispatch] = useRemoteReducer('storywriter.state', reducer, DEFAULT_STATE);
|
||||
|
||||
const value = useMemo<AppState>(() => ({
|
||||
stories: state.stories,
|
||||
|
|
|
|||
Loading…
Reference in New Issue