Spaces:
Runtime error
Runtime error
| "use client"; | |
| import { useEffect, useState } from "react"; | |
| import { Pencil2Icon } from "@radix-ui/react-icons"; | |
| import { Message } from "ai/react"; | |
| import Image from "next/image"; | |
| import OllamaLogo from "../../public/ollama.png"; | |
| import { ChatOptions } from "./chat/chat-options"; | |
| import SidebarTabs from "./sidebar-tabs"; | |
| import Link from "next/link"; | |
| interface SidebarProps { | |
| isCollapsed: boolean; | |
| onClick?: () => void; | |
| isMobile: boolean; | |
| chatId: string; | |
| setChatId: React.Dispatch<React.SetStateAction<string>>; | |
| chatOptions: ChatOptions; | |
| setChatOptions: React.Dispatch<React.SetStateAction<ChatOptions>>; | |
| } | |
| interface Chats { | |
| [key: string]: { chatId: string; messages: Message[] }[]; | |
| } | |
| export function Sidebar({ | |
| isCollapsed, | |
| isMobile, | |
| chatId, | |
| setChatId, | |
| chatOptions, | |
| setChatOptions, | |
| }: SidebarProps) { | |
| const [localChats, setLocalChats] = useState<Chats>({}); | |
| const [isLoading, setIsLoading] = useState(true); | |
| useEffect(() => { | |
| setLocalChats(getLocalstorageChats()); | |
| const handleStorageChange = () => { | |
| setLocalChats(getLocalstorageChats()); | |
| }; | |
| window.addEventListener("storage", handleStorageChange); | |
| return () => { | |
| window.removeEventListener("storage", handleStorageChange); | |
| }; | |
| }, [chatId]); | |
| const getLocalstorageChats = (): Chats => { | |
| const chats = Object.keys(localStorage).filter((key) => | |
| key.startsWith("chat_") | |
| ); | |
| if (chats.length === 0) { | |
| setIsLoading(false); | |
| } | |
| // Map through the chats and return an object with chatId and messages | |
| const chatObjects = chats.map((chat) => { | |
| const item = localStorage.getItem(chat); | |
| return item | |
| ? { chatId: chat, messages: JSON.parse(item) } | |
| : { chatId: "", messages: [] }; | |
| }); | |
| // Sort chats by the createdAt date of the first message of each chat | |
| chatObjects.sort((a, b) => { | |
| const aDate = new Date(a.messages[0].createdAt); | |
| const bDate = new Date(b.messages[0].createdAt); | |
| return bDate.getTime() - aDate.getTime(); | |
| }); | |
| const groupChatsByDate = ( | |
| chats: { chatId: string; messages: Message[] }[] | |
| ) => { | |
| const today = new Date(); | |
| const yesterday = new Date(today); | |
| yesterday.setDate(yesterday.getDate() - 1); | |
| const groupedChats: Chats = {}; | |
| chats.forEach((chat) => { | |
| const createdAt = new Date(chat.messages[0].createdAt ?? ""); | |
| const diffInDays = Math.floor( | |
| (today.getTime() - createdAt.getTime()) / (1000 * 3600 * 24) | |
| ); | |
| let group: string; | |
| if (diffInDays === 0) { | |
| group = "Today"; | |
| } else if (diffInDays === 1) { | |
| group = "Yesterday"; | |
| } else if (diffInDays <= 7) { | |
| group = "Previous 7 Days"; | |
| } else if (diffInDays <= 30) { | |
| group = "Previous 30 Days"; | |
| } else { | |
| group = "Older"; | |
| } | |
| if (!groupedChats[group]) { | |
| groupedChats[group] = []; | |
| } | |
| groupedChats[group].push(chat); | |
| }); | |
| return groupedChats; | |
| }; | |
| setIsLoading(false); | |
| const groupedChats = groupChatsByDate(chatObjects); | |
| return groupedChats; | |
| }; | |
| const handleDeleteChat = (chatId: string) => { | |
| localStorage.removeItem(chatId); | |
| setLocalChats(getLocalstorageChats()); | |
| }; | |
| return ( | |
| <div | |
| data-collapsed={isCollapsed} | |
| className="relative justify-between group bg-accent/20 dark:bg-card/35 flex flex-col h-full gap-4 data-[collapsed=true]:p-0 data-[collapsed=true]:hidden" | |
| > | |
| <div className="sticky left-0 right-0 top-0 z-20 p-1 rounded-sm m-2"> | |
| <Link | |
| className="flex w-full h-10 text-sm font-medium items-center | |
| border border-input bg-background hover:bg-accent hover:text-accent-foreground | |
| px-2 py-2 rounded-sm" | |
| href="/" | |
| onClick={() => { | |
| setChatId(""); | |
| }} | |
| > | |
| <div className="flex gap-3 p-2 items-center justify-between w-full"> | |
| <div className="flex align-start gap-2"> | |
| {!isCollapsed && !isMobile && ( | |
| <Image | |
| src={OllamaLogo} | |
| alt="AI" | |
| width={14} | |
| height={14} | |
| className="dark:invert 2xl:block" | |
| /> | |
| )} | |
| <span>New chat</span> | |
| </div> | |
| <Pencil2Icon className="w-4 h-4" /> | |
| </div> | |
| </Link> | |
| </div> | |
| <SidebarTabs | |
| isLoading={isLoading} | |
| localChats={localChats} | |
| selectedChatId={chatId} | |
| chatOptions={chatOptions} | |
| setChatOptions={setChatOptions} | |
| handleDeleteChat={handleDeleteChat} | |
| /> | |
| </div> | |
| ); | |
| } | |