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

165 lines
5.3 KiB
TypeScript

import { createContext } from "preact";
import { useEffect, useMemo, useState } from "preact/hooks";
import { MessageTools, type IMessage } from "../messages";
interface IContext {
connectionUrl: string;
input: string;
name: string;
messages: IMessage[];
triggerNext: boolean;
}
interface IActions {
setConnectionUrl: (url: string) => void;
setInput: (url: string) => void;
setName: (name: string) => void;
setTriggerNext: (triggerNext: boolean) => void;
setMessages: (messages: IMessage[]) => void;
addMessage: (content: string, role: IMessage['role'], triggerNext?: boolean) => void;
editMessage: (index: number, content: string, triggerNext?: boolean) => void;
deleteMessage: (index: number) => void;
setCurrentSwipe: (index: number, swipe: number) => void;
addSwipe: (index: number, content: string) => void;
continueMessage: () => void;
}
const SAVE_KEY = 'ai_game_save_state';
export const saveContext = (context: IContext) => {
localStorage.setItem(SAVE_KEY, JSON.stringify({
...context,
triggerNext: false,
}));
}
export const loadContext = (): IContext => {
const defaultContext: IContext = {
connectionUrl: 'http://192.168.10.102:5001',
input: '',
name: 'Maya',
messages: [],
triggerNext: false,
};
let loadedContext: Partial<IContext> = {};
try {
const json = localStorage.getItem(SAVE_KEY);
if (json) {
loadedContext = JSON.parse(json);
}
} catch { }
return { ...defaultContext, ...loadedContext };
}
export type IStateContext = IContext & IActions;
export const StateContext = createContext<IStateContext>({} as IStateContext);
export const StateContextProvider = ({ children }: { children?: any }) => {
const loadedContext = useMemo(() => loadContext(), []);
const [connectionUrl, setConnectionUrl] = useState(loadedContext.connectionUrl);
const [input, setInput] = useState(loadedContext.input);
const [name, setName] = useState(loadedContext.name);
const [messages, setMessages] = useState(loadedContext.messages);
const [triggerNext, setTriggerNext] = useState(false);
const actions: IActions = useMemo(() => ({
setConnectionUrl,
setInput,
setName,
setTriggerNext,
setMessages: (newMessages) => setMessages(newMessages.slice()),
addMessage: (content, role, triggerNext = false) => {
setMessages(messages => [
...messages,
MessageTools.create(content, role),
]);
setTriggerNext(triggerNext);
},
editMessage: (index, content, triggerNext = false) => {
setMessages(messages => MessageTools.updateContent(messages, index, content));
setTriggerNext(triggerNext);
},
deleteMessage: (index) => setMessages(messages =>
messages.filter((_, i) => i !== index)
),
setCurrentSwipe: (index, currentSwipe) => {
let shouldTrigger = false;
setMessages(messages =>
messages.map(
(message, i) => {
if (i === index) {
const swipes = message.swipes.slice();
const latestSwipe = swipes.at(-1);
if (currentSwipe >= swipes.length) {
if (latestSwipe.content.length > 0) {
currentSwipe = swipes.length;
swipes.push({ content: '' });
} else {
currentSwipe = swipes.length - 1;
}
shouldTrigger = true;
} else while (currentSwipe < 0) {
currentSwipe += swipes.length;
}
return {
...message, swipes, currentSwipe
};
} else {
return message;
}
}
)
);
setTriggerNext(shouldTrigger);
},
addSwipe: (index, content) => setMessages(messages =>
messages.map(
(message, i) => {
if (i === index) {
const swipes = [...message.swipes, { content }];
return {
...message,
swipes,
currentSwipe: swipes.length - 1,
};
} else {
return message;
}
}
)
),
continueMessage: () => setTriggerNext(true),
}), []);
const rawContext: IContext = {
connectionUrl,
input,
name,
messages,
triggerNext,
};
const context = useMemo(() => rawContext, Object.values(rawContext));
useEffect(() => {
saveContext(context);
}, [context]);
const value = useMemo(() => ({ ...context, ...actions }), [context, actions])
return (
<StateContext.Provider value={value}>
{children}
</StateContext.Provider>
);
}