chat-with-gpt/app/src/context.tsx

186 lines
5.7 KiB
TypeScript
Raw Normal View History

2023-03-06 13:30:58 +00:00
import React, { useState, useRef, useMemo, useEffect, useCallback } from "react";
2023-03-08 21:30:11 +00:00
import { useLocation, useNavigate, useParams } from "react-router-dom";
2023-03-06 13:30:58 +00:00
import { backend } from "./backend";
import ChatManagerInstance, { ChatManager } from "./chat-manager";
2023-03-10 22:00:37 +00:00
import store, { useAppDispatch } from "./store";
import { openOpenAIApiKeyPanel } from "./store/settings-ui";
import { Message } from "./types";
2023-03-08 21:30:11 +00:00
import { useChat, UseChatResult } from "./use-chat";
2023-03-06 13:30:58 +00:00
export interface Context {
authenticated: boolean;
chat: ChatManager;
2023-03-08 21:30:11 +00:00
id: string | undefined | null;
currentChat: UseChatResult;
2023-03-14 11:00:40 +00:00
isHome: boolean;
2023-03-08 21:30:11 +00:00
isShare: boolean;
generating: boolean;
onNewMessage: (message?: string) => Promise<boolean>;
2023-03-09 09:38:59 +00:00
regenerateMessage: (message: Message) => Promise<boolean>;
editMessage: (message: Message, content: string) => Promise<boolean>;
2023-03-06 13:30:58 +00:00
}
const AppContext = React.createContext<Context>({} as any);
export function useCreateAppContext(): Context {
2023-03-08 21:30:11 +00:00
const { id } = useParams();
2023-03-10 22:00:37 +00:00
const dispatch = useAppDispatch();
2023-03-08 21:30:11 +00:00
const pathname = useLocation().pathname;
2023-03-14 11:00:40 +00:00
const isHome = pathname === '/';
2023-03-08 21:30:11 +00:00
const isShare = pathname.startsWith('/s/');
const navigate = useNavigate();
2023-03-09 09:38:59 +00:00
2023-03-08 21:30:11 +00:00
const chatManager = useRef(ChatManagerInstance);
const currentChat = useChat(chatManager.current, id, isShare);
2023-03-09 19:50:57 +00:00
const [authenticated, setAuthenticated] = useState(backend.current?.isAuthenticated || false);
2023-03-06 13:30:58 +00:00
const updateAuth = useCallback((authenticated: boolean) => setAuthenticated(authenticated), []);
useEffect(() => {
2023-03-09 19:50:57 +00:00
backend.current?.on('authenticated', updateAuth);
2023-03-06 13:30:58 +00:00
return () => {
2023-03-09 19:50:57 +00:00
backend.current?.off('authenticated', updateAuth)
2023-03-06 13:30:58 +00:00
};
2023-03-09 19:50:57 +00:00
}, [updateAuth]);
2023-03-06 13:30:58 +00:00
2023-03-08 21:30:11 +00:00
const onNewMessage = useCallback(async (message?: string) => {
if (isShare) {
return false;
}
if (!message?.trim().length) {
return false;
}
2023-03-10 22:00:37 +00:00
const openaiApiKey = store.getState().apiKeys.openAIApiKey;
2023-03-08 21:30:11 +00:00
if (!openaiApiKey) {
2023-03-10 22:00:37 +00:00
dispatch(openOpenAIApiKeyPanel());
2023-03-08 21:30:11 +00:00
return false;
}
2023-03-10 22:00:37 +00:00
const parameters = store.getState().parameters;
2023-03-08 21:30:11 +00:00
if (id) {
await chatManager.current.sendMessage({
chatID: id,
content: message.trim(),
requestedParameters: {
...parameters,
apiKey: openaiApiKey,
},
parentID: currentChat.leaf?.id,
});
} else {
const id = await chatManager.current.createChat();
await chatManager.current.sendMessage({
chatID: id,
content: message.trim(),
requestedParameters: {
...parameters,
apiKey: openaiApiKey,
},
parentID: currentChat.leaf?.id,
});
navigate('/chat/' + id);
}
return true;
2023-03-10 22:00:37 +00:00
}, [dispatch, chatManager, id, currentChat.leaf, navigate, isShare]);
2023-03-08 21:30:11 +00:00
2023-03-09 09:38:59 +00:00
const regenerateMessage = useCallback(async (message: Message) => {
if (isShare) {
return false;
}
2023-03-10 22:00:37 +00:00
const openaiApiKey = store.getState().apiKeys.openAIApiKey;
2023-03-09 09:38:59 +00:00
if (!openaiApiKey) {
2023-03-10 22:00:37 +00:00
dispatch(openOpenAIApiKeyPanel());
2023-03-09 09:38:59 +00:00
return false;
}
2023-03-10 22:00:37 +00:00
const parameters = store.getState().parameters;
2023-03-09 09:38:59 +00:00
await chatManager.current.regenerate(message, {
...parameters,
apiKey: openaiApiKey,
});
return true;
2023-03-10 22:00:37 +00:00
}, [dispatch, chatManager, isShare]);
2023-03-09 09:38:59 +00:00
const editMessage = useCallback(async (message: Message, content: string) => {
if (isShare) {
return false;
}
if (!content?.trim().length) {
return false;
}
2023-03-10 22:00:37 +00:00
const openaiApiKey = store.getState().apiKeys.openAIApiKey;
2023-03-09 09:38:59 +00:00
if (!openaiApiKey) {
2023-03-10 22:00:37 +00:00
dispatch(openOpenAIApiKeyPanel());
2023-03-09 09:38:59 +00:00
return false;
}
2023-03-10 22:00:37 +00:00
const parameters = store.getState().parameters;
2023-03-09 09:38:59 +00:00
if (id) {
await chatManager.current.sendMessage({
chatID: id,
content: content.trim(),
requestedParameters: {
...parameters,
apiKey: openaiApiKey,
},
parentID: message.parentID,
});
} else {
const id = await chatManager.current.createChat();
await chatManager.current.sendMessage({
chatID: id,
content: content.trim(),
requestedParameters: {
...parameters,
apiKey: openaiApiKey,
},
parentID: message.parentID,
});
navigate('/chat/' + id);
}
return true;
2023-03-10 22:00:37 +00:00
}, [dispatch, chatManager, id, isShare, navigate]);
2023-03-09 09:38:59 +00:00
2023-03-14 11:00:40 +00:00
const generating = currentChat?.messagesToDisplay?.length > 0
? !currentChat.messagesToDisplay[currentChat.messagesToDisplay.length - 1].done
: false;
2023-03-06 13:30:58 +00:00
const context = useMemo<Context>(() => ({
authenticated,
2023-03-08 21:30:11 +00:00
id,
chat: chatManager.current,
currentChat,
2023-03-14 11:00:40 +00:00
isHome,
2023-03-08 21:30:11 +00:00
isShare,
generating,
onNewMessage,
2023-03-09 09:38:59 +00:00
regenerateMessage,
editMessage,
2023-03-10 22:00:37 +00:00
}), [chatManager, authenticated, generating, onNewMessage, regenerateMessage, editMessage, currentChat, id, isShare]);
2023-03-06 13:30:58 +00:00
return context;
}
export function useAppContext() {
return React.useContext(AppContext);
}
export function AppContextProvider(props: { children: React.ReactNode }) {
const context = useCreateAppContext();
return <AppContext.Provider value={context}>{props.children}</AppContext.Provider>;
}