chat-with-gpt/src/context.tsx

200 lines
6.2 KiB
TypeScript
Raw Normal View History

2023-03-08 21:30:11 +00:00
import { useDebouncedValue } from "@mantine/hooks";
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";
import { defaultElevenLabsVoiceID } from "./elevenlabs";
2023-03-08 21:30:11 +00:00
import { loadParameters, saveParameters } from "./parameters";
import { Parameters } from "./types";
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;
isShare: boolean;
2023-03-06 13:30:58 +00:00
apiKeys: {
openai: string | undefined | null;
setOpenAIApiKey: (apiKey: string | null) => void;
elevenlabs: string | undefined | null;
setElevenLabsApiKey: (apiKey: string | null) => void;
};
settings: {
tab: string | undefined | null;
option: string | undefined | null;
open: (tab: string, option?: string | undefined | null) => void;
close: () => void;
};
voice: {
id: string;
setVoiceID: (id: string) => void;
2023-03-08 21:30:11 +00:00
};
generating: boolean;
message: string;
parameters: Parameters;
setMessage: (message: string) => void;
setParameters: (parameters: Parameters) => void;
onNewMessage: (message?: 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();
const pathname = useLocation().pathname;
const isShare = pathname.startsWith('/s/');
const navigate = useNavigate();
const chatManager = useRef(ChatManagerInstance);
const currentChat = useChat(chatManager.current, id, isShare);
2023-03-06 13:30:58 +00:00
const [authenticated, setAuthenticated] = useState(backend?.isAuthenticated || false);
const updateAuth = useCallback((authenticated: boolean) => setAuthenticated(authenticated), []);
useEffect(() => {
backend?.on('authenticated', updateAuth);
return () => {
backend?.off('authenticated', updateAuth)
};
}, [backend]);
const [openaiApiKey, setOpenAIApiKey] = useState<string | null>(
localStorage.getItem('openai-api-key') || ''
);
const [elevenLabsApiKey, setElevenLabsApiKey] = useState<string | null>(
localStorage.getItem('elevenlabs-api-key') || ''
);
useEffect(() => {
2023-03-08 21:30:11 +00:00
if (openaiApiKey) {
localStorage.setItem('openai-api-key', openaiApiKey || '');
}
2023-03-06 13:30:58 +00:00
}, [openaiApiKey]);
useEffect(() => {
2023-03-08 21:30:11 +00:00
if (elevenLabsApiKey) {
localStorage.setItem('elevenlabs-api-key', elevenLabsApiKey || '');
}
2023-03-06 13:30:58 +00:00
}, [elevenLabsApiKey]);
const [settingsTab, setSettingsTab] = useState<string | null | undefined>();
const [option, setOption] = useState<string | null | undefined>();
const [voiceID, setVoiceID] = useState(localStorage.getItem('voice-id') || defaultElevenLabsVoiceID);
useEffect(() => {
localStorage.setItem('voice-id', voiceID);
}, [voiceID]);
2023-03-08 21:30:11 +00:00
const [generating, setGenerating] = useState(false);
const [message, setMessage] = useState('');
const [_parameters, setParameters] = useState<Parameters>(loadParameters(id));
useEffect(() => {
setParameters(loadParameters(id));
}, [id]);
const [parameters] = useDebouncedValue(_parameters, 2000);
useEffect(() => {
if (id) {
saveParameters(id, parameters);
}
saveParameters('', parameters);
}, [id, parameters]);
const onNewMessage = useCallback(async (message?: string) => {
if (isShare) {
return false;
}
if (!message?.trim().length) {
return false;
}
if (!openaiApiKey) {
setSettingsTab('user');
setOption('openai-api-key');
return false;
}
setGenerating(true);
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);
}
setTimeout(() => setGenerating(false), 4000);
return true;
}, [chatManager, openaiApiKey, id, parameters, message, currentChat.leaf]);
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,
isShare,
2023-03-06 13:30:58 +00:00
apiKeys: {
openai: openaiApiKey,
elevenlabs: elevenLabsApiKey,
setOpenAIApiKey,
setElevenLabsApiKey,
},
settings: {
tab: settingsTab,
option: option,
open: (tab: string, option?: string | undefined | null) => {
setSettingsTab(tab);
setOption(option);
},
close: () => {
setSettingsTab(null);
setOption(null);
},
},
voice: {
id: voiceID,
setVoiceID,
},
2023-03-08 21:30:11 +00:00
generating,
message,
parameters,
setMessage,
setParameters,
onNewMessage,
}), [chatManager, authenticated, openaiApiKey, elevenLabsApiKey, settingsTab, option, voiceID,
generating, message, parameters, onNewMessage, currentChat]);
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>;
}