|
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> |
|
</> |
|
); |
|
} |
|
|