File size: 2,353 Bytes
987282d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"use client"
import { FC, useEffect, useRef, useState } from "react"
import { Socket } from "socket.io-client"
import { ClientToServerEvents, ServerToClientEvents } from "../../lib/socket"

type ChatMessage = {
  id: string
  userId: string
  name: string
  text: string
  ts: number
}

interface Props {
  socket: Socket<ServerToClientEvents, ClientToServerEvents>
  className?: string
}

const ChatPanel: FC<Props> = ({ socket, className }) => {
  const [messages, _setMessages] = useState<ChatMessage[]>([])
  const [text, setText] = useState("")
  const messagesRef = useRef(messages)
  const setMessages = (m: ChatMessage[]) => {
    messagesRef.current = m
    _setMessages(m)
  }

  useEffect(() => {
    const onHistory = (history: ChatMessage[]) => {
      setMessages(history)
    }
    const onNew = (msg: ChatMessage) => {
      setMessages([...messagesRef.current, msg].slice(-200))
    }

    socket.on("chatHistory", onHistory)
    socket.on("chatNew", onNew)
    return () => {
      socket.off("chatHistory", onHistory)
      socket.off("chatNew", onNew)
    }
  }, [socket])

  const send = () => {
    const trimmed = text.trim()
    if (!trimmed) return
    socket.emit("chatMessage", trimmed)
    setText("")
  }

  return (
    <div className={className ?? "flex flex-col h-64 border rounded-md"}>
      <div className="flex-1 overflow-y-auto p-2 space-y-2 bg-neutral-900/30">
        {messages.map((m) => (
          <div key={m.id} className="text-sm">
            <span className="font-semibold">{m.name}</span>
            <span className="opacity-60"> • {new Date(m.ts).toLocaleTimeString()}</span>
            <div className="break-words">{m.text}</div>
          </div>
        ))}
        {messages.length === 0 && <div className="opacity-60 text-sm">No messages yet</div>}
      </div>
      <div className="p-2 flex gap-2">
        <input
          className="input flex-1 bg-neutral-800 p-2 rounded-md outline-none"
          placeholder="Type a message…"
          value={text}
          onChange={(e) => setText(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter") send()
          }}
        />
        <button className="btn bg-primary-700 hover:bg-primary-600 px-3 rounded-md" onClick={send}>
          Send
        </button>
      </div>
    </div>
  )
}

export default ChatPanel