Spaces:
Sleeping
Sleeping
| 'use client'; | |
| // import { ChatList } from '@/components/chat/ChatList'; | |
| import Composer from '@/components/chat/Composer'; | |
| import useVisionAgent from '@/lib/hooks/useVisionAgent'; | |
| import { useScrollAnchor } from '@/lib/hooks/useScrollAnchor'; | |
| import { useEffect } from 'react'; | |
| import { ChatWithMessages } from '@/lib/types'; | |
| import { ChatMessage } from './ChatMessage'; | |
| import { Button } from '../ui/Button'; | |
| import { cn } from '@/lib/utils'; | |
| import { IconArrowDown } from '../ui/Icons'; | |
| import { dbPostCreateMessage } from '@/lib/db/functions'; | |
| import { Card } from '../ui/Card'; | |
| export interface ChatListProps { | |
| chat: ChatWithMessages; | |
| userId?: string | null; | |
| } | |
| export const SCROLL_BOTTOM = 120; | |
| const ChatList: React.FC<ChatListProps> = ({ chat, userId }) => { | |
| const { id, messages: dbMessages, userId: chatUserId } = chat; | |
| const { isLoading, data } = useVisionAgent(chat); | |
| // Only login and chat owner can compose | |
| const canCompose = !chatUserId || userId === chatUserId; | |
| const { messagesRef, scrollRef, visibilityRef, isVisible, scrollToBottom } = | |
| useScrollAnchor(SCROLL_BOTTOM); | |
| // Scroll to bottom on init | |
| useEffect(() => { | |
| scrollToBottom(); | |
| }, [scrollToBottom]); | |
| return ( | |
| <Card | |
| className="size-full max-w-5xl overflow-auto relative" | |
| ref={scrollRef} | |
| > | |
| <div className="overflow-auto h-full p-4 z-10" ref={messagesRef}> | |
| {dbMessages.map((message, index) => { | |
| const isLastMessage = index === dbMessages.length - 1; | |
| return ( | |
| <ChatMessage | |
| key={message.id} | |
| message={message} | |
| loading={isLastMessage && isLoading} | |
| wipAssistantMessage={ | |
| isLastMessage && data.length > 0 ? data : undefined | |
| } | |
| /> | |
| ); | |
| })} | |
| <div | |
| className="w-full" | |
| style={{ height: SCROLL_BOTTOM }} | |
| ref={visibilityRef} | |
| /> | |
| </div> | |
| {canCompose && ( | |
| <div className="absolute bottom-4 w-full"> | |
| <Composer | |
| // Use the last message mediaUrl as the initial mediaUrl | |
| initMediaUrl={dbMessages[dbMessages.length - 1]?.mediaUrl} | |
| disabled={isLoading} | |
| onSubmit={async ({ input, mediaUrl: newMediaUrl }) => { | |
| const messageInput = { | |
| prompt: input, | |
| mediaUrl: newMediaUrl, | |
| }; | |
| await dbPostCreateMessage(id, messageInput); | |
| }} | |
| /> | |
| </div> | |
| )} | |
| {/* Scroll to bottom Icon */} | |
| <Button | |
| size="icon" | |
| className={cn( | |
| 'absolute bottom-3 right-3 transition-opacity duration-300 size-6', | |
| isVisible ? 'opacity-0' : 'opacity-100', | |
| )} | |
| onClick={() => scrollToBottom()} | |
| > | |
| <IconArrowDown className="size-3" /> | |
| </Button> | |
| </Card> | |
| ); | |
| }; | |
| export default ChatList; | |