Spaces:
Runtime error
Runtime error
import { useCallback, useContext, useEffect } from 'react'; | |
import { useTranslation } from 'next-i18next'; | |
import { useCreateReducer } from '@/hooks/useCreateReducer'; | |
import { DEFAULT_SYSTEM_PROMPT, DEFAULT_TEMPERATURE } from '@/utils/app/const'; | |
import { saveConversation, saveConversations } from '@/utils/app/conversation'; | |
import { saveFolders } from '@/utils/app/folders'; | |
import { exportData, importData } from '@/utils/app/importExport'; | |
import { Conversation } from '@/types/chat'; | |
import { LatestExportFormat, SupportedExportFormats } from '@/types/export'; | |
import { OpenAIModels } from '@/types/openai'; | |
import { PluginKey } from '@/types/plugin'; | |
import HomeContext from '@/pages/api/home/home.context'; | |
import { ChatFolders } from './components/ChatFolders'; | |
import { ChatbarSettings } from './components/ChatbarSettings'; | |
import { Conversations } from './components/Conversations'; | |
import Sidebar from '../Sidebar'; | |
import ChatbarContext from './Chatbar.context'; | |
import { ChatbarInitialState, initialState } from './Chatbar.state'; | |
import { v4 as uuidv4 } from 'uuid'; | |
export const Chatbar = () => { | |
const { t } = useTranslation('sidebar'); | |
const chatBarContextValue = useCreateReducer<ChatbarInitialState>({ | |
initialState, | |
}); | |
const { | |
state: { conversations, showChatbar, defaultModelId, folders, pluginKeys }, | |
dispatch: homeDispatch, | |
handleCreateFolder, | |
handleNewConversation, | |
handleUpdateConversation, | |
} = useContext(HomeContext); | |
const { | |
state: { searchTerm, filteredConversations }, | |
dispatch: chatDispatch, | |
} = chatBarContextValue; | |
const handleApiKeyChange = useCallback( | |
(apiKey: string) => { | |
homeDispatch({ field: 'apiKey', value: apiKey }); | |
localStorage.setItem('apiKey', apiKey); | |
}, | |
[homeDispatch], | |
); | |
const handlePluginKeyChange = (pluginKey: PluginKey) => { | |
if (pluginKeys.some((key) => key.pluginId === pluginKey.pluginId)) { | |
const updatedPluginKeys = pluginKeys.map((key) => { | |
if (key.pluginId === pluginKey.pluginId) { | |
return pluginKey; | |
} | |
return key; | |
}); | |
homeDispatch({ field: 'pluginKeys', value: updatedPluginKeys }); | |
localStorage.setItem('pluginKeys', JSON.stringify(updatedPluginKeys)); | |
} else { | |
homeDispatch({ field: 'pluginKeys', value: [...pluginKeys, pluginKey] }); | |
localStorage.setItem( | |
'pluginKeys', | |
JSON.stringify([...pluginKeys, pluginKey]), | |
); | |
} | |
}; | |
const handleClearPluginKey = (pluginKey: PluginKey) => { | |
const updatedPluginKeys = pluginKeys.filter( | |
(key) => key.pluginId !== pluginKey.pluginId, | |
); | |
if (updatedPluginKeys.length === 0) { | |
homeDispatch({ field: 'pluginKeys', value: [] }); | |
localStorage.removeItem('pluginKeys'); | |
return; | |
} | |
homeDispatch({ field: 'pluginKeys', value: updatedPluginKeys }); | |
localStorage.setItem('pluginKeys', JSON.stringify(updatedPluginKeys)); | |
}; | |
const handleExportData = () => { | |
exportData(); | |
}; | |
const handleImportConversations = (data: SupportedExportFormats) => { | |
const { history, folders, prompts }: LatestExportFormat = importData(data); | |
homeDispatch({ field: 'conversations', value: history }); | |
homeDispatch({ | |
field: 'selectedConversation', | |
value: history[history.length - 1], | |
}); | |
homeDispatch({ field: 'folders', value: folders }); | |
homeDispatch({ field: 'prompts', value: prompts }); | |
window.location.reload(); | |
}; | |
const handleClearConversations = () => { | |
defaultModelId && | |
homeDispatch({ | |
field: 'selectedConversation', | |
value: { | |
id: uuidv4(), | |
name: t('New Conversation'), | |
messages: [], | |
model: OpenAIModels[defaultModelId], | |
prompt: DEFAULT_SYSTEM_PROMPT, | |
temperature: DEFAULT_TEMPERATURE, | |
folderId: null, | |
}, | |
}); | |
homeDispatch({ field: 'conversations', value: [] }); | |
localStorage.removeItem('conversationHistory'); | |
localStorage.removeItem('selectedConversation'); | |
const updatedFolders = folders.filter((f) => f.type !== 'chat'); | |
homeDispatch({ field: 'folders', value: updatedFolders }); | |
saveFolders(updatedFolders); | |
}; | |
const handleDeleteConversation = (conversation: Conversation) => { | |
const updatedConversations = conversations.filter( | |
(c) => c.id !== conversation.id, | |
); | |
homeDispatch({ field: 'conversations', value: updatedConversations }); | |
chatDispatch({ field: 'searchTerm', value: '' }); | |
saveConversations(updatedConversations); | |
if (updatedConversations.length > 0) { | |
homeDispatch({ | |
field: 'selectedConversation', | |
value: updatedConversations[updatedConversations.length - 1], | |
}); | |
saveConversation(updatedConversations[updatedConversations.length - 1]); | |
} else { | |
defaultModelId && | |
homeDispatch({ | |
field: 'selectedConversation', | |
value: { | |
id: uuidv4(), | |
name: t('New Conversation'), | |
messages: [], | |
model: OpenAIModels[defaultModelId], | |
prompt: DEFAULT_SYSTEM_PROMPT, | |
temperature: DEFAULT_TEMPERATURE, | |
folderId: null, | |
}, | |
}); | |
localStorage.removeItem('selectedConversation'); | |
} | |
}; | |
const handleToggleChatbar = () => { | |
homeDispatch({ field: 'showChatbar', value: !showChatbar }); | |
localStorage.setItem('showChatbar', JSON.stringify(!showChatbar)); | |
}; | |
const handleDrop = (e: any) => { | |
if (e.dataTransfer) { | |
const conversation = JSON.parse(e.dataTransfer.getData('conversation')); | |
handleUpdateConversation(conversation, { key: 'folderId', value: 0 }); | |
chatDispatch({ field: 'searchTerm', value: '' }); | |
e.target.style.background = 'none'; | |
} | |
}; | |
useEffect(() => { | |
if (searchTerm) { | |
chatDispatch({ | |
field: 'filteredConversations', | |
value: conversations.filter((conversation) => { | |
const searchable = | |
conversation.name.toLocaleLowerCase() + | |
' ' + | |
conversation.messages.map((message) => message.content).join(' '); | |
return searchable.toLowerCase().includes(searchTerm.toLowerCase()); | |
}), | |
}); | |
} else { | |
chatDispatch({ | |
field: 'filteredConversations', | |
value: conversations, | |
}); | |
} | |
}, [searchTerm, conversations]); | |
return ( | |
<ChatbarContext.Provider | |
value={{ | |
...chatBarContextValue, | |
handleDeleteConversation, | |
handleClearConversations, | |
handleImportConversations, | |
handleExportData, | |
handlePluginKeyChange, | |
handleClearPluginKey, | |
handleApiKeyChange, | |
}} | |
> | |
<Sidebar<Conversation> | |
side={'left'} | |
isOpen={showChatbar} | |
addItemButtonTitle={t('New chat')} | |
itemComponent={<Conversations conversations={filteredConversations} />} | |
folderComponent={<ChatFolders searchTerm={searchTerm} />} | |
items={filteredConversations} | |
searchTerm={searchTerm} | |
handleSearchTerm={(searchTerm: string) => | |
chatDispatch({ field: 'searchTerm', value: searchTerm }) | |
} | |
toggleOpen={handleToggleChatbar} | |
handleCreateItem={handleNewConversation} | |
handleCreateFolder={() => handleCreateFolder(t('New folder'), 'chat')} | |
handleDrop={handleDrop} | |
footerComponent={<ChatbarSettings />} | |
/> | |
</ChatbarContext.Provider> | |
); | |
}; | |