165 lines
5.3 KiB
TypeScript
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>
|
|
);
|
|
} |