File size: 2,917 Bytes
edd2230
 
4af6326
 
 
 
 
bc1cf4e
4af6326
 
 
 
 
 
3ba9c0c
4af6326
 
db06845
3ba9c0c
 
4af6326
 
db06845
 
c7e97b5
4af6326
db06845
 
 
4af6326
 
 
c7e97b5
4af6326
 
c7e97b5
4af6326
f80b091
4af6326
 
 
 
 
db06845
 
 
 
 
 
 
c7e97b5
 
 
db06845
 
 
4af6326
 
 
 
 
 
db06845
 
 
 
 
 
 
 
 
 
 
c7e97b5
db06845
 
 
 
4af6326
 
 
 
 
 
 
 
 
 
 
 
f80b091
4af6326
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
'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;