| import { useEffect, useState } from 'react'; | |
| import { classNames } from '../utils/misc'; | |
| import { Conversation } from '../utils/types'; | |
| import StorageUtils from '../utils/storage'; | |
| import { useNavigate, useParams } from 'react-router'; | |
| export default function Sidebar() { | |
| const params = useParams(); | |
| const navigate = useNavigate(); | |
| const [conversations, setConversations] = useState<Conversation[]>([]); | |
| const [currConv, setCurrConv] = useState<Conversation | null>(null); | |
| useEffect(() => { | |
| StorageUtils.getOneConversation(params.convId ?? '').then(setCurrConv); | |
| }, [params.convId]); | |
| useEffect(() => { | |
| const handleConversationChange = async () => { | |
| setConversations(await StorageUtils.getAllConversations()); | |
| }; | |
| StorageUtils.onConversationChanged(handleConversationChange); | |
| handleConversationChange(); | |
| return () => { | |
| StorageUtils.offConversationChanged(handleConversationChange); | |
| }; | |
| }, []); | |
| return ( | |
| <> | |
| <input | |
| id="toggle-drawer" | |
| type="checkbox" | |
| className="drawer-toggle" | |
| defaultChecked | |
| /> | |
| <div className="drawer-side h-screen lg:h-screen z-50 lg:max-w-64"> | |
| <label | |
| htmlFor="toggle-drawer" | |
| aria-label="close sidebar" | |
| className="drawer-overlay" | |
| ></label> | |
| <div className="flex flex-col bg-base-200 min-h-full max-w-64 py-4 px-4"> | |
| <div className="flex flex-row items-center justify-between mb-4 mt-4"> | |
| <h2 className="font-bold ml-4">Conversations</h2> | |
| {/* close sidebar button */} | |
| <label htmlFor="toggle-drawer" className="btn btn-ghost lg:hidden"> | |
| <svg | |
| xmlns="http://www.w3.org/2000/svg" | |
| width="16" | |
| height="16" | |
| fill="currentColor" | |
| className="bi bi-arrow-bar-left" | |
| viewBox="0 0 16 16" | |
| > | |
| <path | |
| fillRule="evenodd" | |
| d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5M10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5" | |
| /> | |
| </svg> | |
| </label> | |
| </div> | |
| {/* list of conversations */} | |
| <div | |
| className={classNames({ | |
| 'btn btn-ghost justify-start': true, | |
| 'btn-active': !currConv, | |
| })} | |
| onClick={() => navigate('/')} | |
| > | |
| + New conversation | |
| </div> | |
| {conversations.map((conv) => ( | |
| <div | |
| key={conv.id} | |
| className={classNames({ | |
| 'btn btn-ghost justify-start font-normal': true, | |
| 'btn-active': conv.id === currConv?.id, | |
| })} | |
| onClick={() => navigate(`/chat/${conv.id}`)} | |
| dir="auto" | |
| > | |
| <span className="truncate">{conv.name}</span> | |
| </div> | |
| ))} | |
| <div className="text-center text-xs opacity-40 mt-auto mx-4"> | |
| Conversations are saved to browser's IndexedDB | |
| </div> | |
| </div> | |
| </div> | |
| </> | |
| ); | |
| } | |