1
0
Fork 0
tsgames/src/games/storywriter/contexts/state.tsx

116 lines
4.2 KiB
TypeScript

import { createContext } from "preact";
import { useContext, useMemo, useReducer } from "preact/hooks";
// ─── Types ────────────────────────────────────────────────────────────────────
export interface Story {
id: string;
title: string;
text: string;
}
// ─── State ───────────────────────────────────────────────────────────────────
interface IState {
stories: Story[];
currentStoryId: string | null;
}
// ─── Actions ─────────────────────────────────────────────────────────────────
type Action =
| { type: 'CREATE_STORY'; title: string }
| { type: 'RENAME_STORY'; id: string; title: string }
| { type: 'EDIT_STORY'; id: string; text: string }
| { type: 'DELETE_STORY'; id: string }
| { type: 'SELECT_STORY'; id: string };
// ─── Initial State ───────────────────────────────────────────────────────────
const DEFAULT_STATE: IState = {
stories: [],
currentStoryId: null,
};
// ─── Reducer ─────────────────────────────────────────────────────────────────
function reducer(state: IState, action: Action): IState {
switch (action.type) {
case 'CREATE_STORY': {
const story: Story = {
id: crypto.randomUUID(),
title: action.title,
text: '',
};
return {
...state,
stories: [...state.stories, story],
currentStoryId: story.id,
};
}
case 'RENAME_STORY': {
return {
...state,
stories: state.stories.map(s =>
s.id === action.id ? { ...s, title: action.title } : s
),
};
}
case 'EDIT_STORY': {
return {
...state,
stories: state.stories.map(s =>
s.id === action.id ? { ...s, text: action.text } : s
),
};
}
case 'DELETE_STORY': {
const remaining = state.stories.filter(s => s.id !== action.id);
const deletingCurrent = state.currentStoryId === action.id;
return {
...state,
stories: remaining,
currentStoryId: deletingCurrent ? null : state.currentStoryId,
};
}
case 'SELECT_STORY': {
return {
...state,
currentStoryId: action.id,
};
}
default:
return state;
}
}
// ─── Context ─────────────────────────────────────────────────────────────────
interface IStateContext {
stories: Story[];
currentStory: Story | null;
dispatch: (action: Action) => void;
}
const StateContext = createContext<IStateContext>({} as IStateContext);
export const useAppState = () => useContext(StateContext);
// ─── Provider ────────────────────────────────────────────────────────────────
export const StateContextProvider = ({ children }: { children?: any }) => {
const [state, dispatch] = useReducer(reducer, DEFAULT_STATE);
const value = useMemo<IStateContext>(() => ({
stories: state.stories,
currentStory: state.stories.find(s => s.id === state.currentStoryId) ?? null,
dispatch,
}), [state]);
return (
<StateContext.Provider value={value}>
{children}
</StateContext.Provider>
);
};