import { createContext } from 'react'; import { createStore, StateCreator } from 'zustand'; import { doesExist, Maybe } from '@apextoaster/js-utils'; import { PaletteMode } from '@mui/material'; import { ReadyState } from 'react-use-websocket'; import { Character, GameEvent, Item, Portal, PromptEvent, Room, World } from './models'; export type LayoutMode = 'horizontal' | 'vertical'; export interface ClientState { autoScroll: boolean; clientId: string; clientName: string; detailEntity: Maybe; eventHistory: Array; layoutMode: LayoutMode; readyState: ReadyState; themeMode: PaletteMode; // setters setAutoScroll: (autoScroll: boolean) => void; setClientId: (clientId: string) => void; setClientName: (name: string) => void; setDetailEntity: (entity: Maybe) => void; setLayoutMode: (mode: LayoutMode) => void; setReadyState: (state: ReadyState) => void; setThemeMode: (mode: PaletteMode) => void; // misc helpers appendEvent: (event: GameEvent) => void; clearDetailEntity: () => void; clearEventHistory: () => void; } export interface WorldState { players: Record; turn: Maybe; world: Maybe; // setters setPlayers: (players: Record) => void; setTurn: (turn: Maybe) => void; setWorld: (world: Maybe) => void; } export interface PlayerState { playerCharacter: Maybe; promptEvent: Maybe; // setters setPlayerCharacter: (character: Maybe) => void; setPromptEvent: (promptEvent: Maybe) => void; // misc helpers isActive: () => boolean; isPlaying: () => boolean; } export type StoreState = ClientState & WorldState & PlayerState; export function createClientStore(): StateCreator { return (set) => ({ autoScroll: true, clientId: '', clientName: '', detailEntity: undefined, eventHistory: [], layoutMode: 'horizontal', readyState: ReadyState.UNINSTANTIATED, themeMode: 'light', setAutoScroll(autoScroll) { set({ autoScroll }); }, setClientId(clientId) { set({ clientId }); }, setClientName(clientName) { set({ clientName }); }, setDetailEntity(detailEntity) { set({ detailEntity }); }, setLayoutMode(mode) { set({ layoutMode: mode }); }, setReadyState(state) { set({ readyState: state }); }, setThemeMode(themeMode) { set({ themeMode }); }, appendEvent(event) { set((state) => { const history = state.eventHistory.concat(event); return { eventHistory: history }; }); }, clearDetailEntity() { set({ detailEntity: undefined }); }, clearEventHistory() { set({ eventHistory: [] }); }, }); } export function createWorldStore(): StateCreator { return (set) => ({ players: {}, turn: undefined, world: undefined, setPlayers: (players) => set({ players }), setTurn: (turn) => set({ turn }), setWorld: (world) => set({ world }), }); } export function createPlayerStore(): StateCreator { return (set) => ({ playerCharacter: undefined, promptEvent: undefined, setPlayerCharacter: (character: Maybe) => set({ playerCharacter: character }), setPromptEvent(promptEvent) { set({ promptEvent }); }, isActive() { return doesExist(this.playerCharacter) && doesExist(this.promptEvent); }, isPlaying() { return doesExist(this.playerCharacter); }, }); } export function createStateStore() { return createStore((...args) => ({ ...createClientStore()(...args), ...createWorldStore()(...args), ...createPlayerStore()(...args), })); } // TODO: make this not global export const store = createStateStore(); export const storeContext = createContext(store);