"use client" import { ArrowLeft, ImagePlus, Lock, Send } from "lucide-react" import Image from "next/image" import Link from "next/link" import { use, useMemo, useRef, useState } from "react" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { ScrollArea } from "@/components/ui/scroll-area" import { sendImageMessage, sendTextMessage } from "@/lib/api/chat" import { cn } from "@/lib/utils" import { notifyInfo } from "@/lib/toast" import { useAuthStore } from "@/store/auth" import { useChatStore } from "@/store/chat" export default function ChatDetailPage({ params }: { params: Promise<{ id: string }> }) { const { id } = use(params) const session = useChatStore((state) => state.sessions.find((item) => item.id === id)) const allMessages = useChatStore((state) => state.messages) // Filter logic runs here via useMemo rather than inside the Zustand selector. // useSyncExternalStore requires a stable snapshot reference on each render. // Inline filter in a selector creates a new array per call and can trigger // infinite re-render loops in Zustand v5 (pmndrs/zustand#1936). const messages = useMemo( () => allMessages.filter((item) => item.sessionId === id), [allMessages, id], ) const [input, setInput] = useState("") const imageInputRef = useRef(null) const { user } = useAuthStore() if (!session) { return (
会话不存在
) } if (!user?.id) { return (
请先登录后查看会话
) } const userId = user.id const isParticipant = session.participants.some((participant) => participant.id === userId) if (!isParticipant) { return (
仅会话参与方可查看并发送消息
) } const other = session.participants.find((p) => p.id !== userId) ?? session.participants[0] return (
{other.name[0]}
{other.name}
{session.type === "order" ? "订单会话" : "咨询会话"} {session.readonly && ( 只读 )}
{messages.map((msg) => { if (msg.type === "system") { return (
{msg.content}
) } const isMine = msg.senderId === userId return (
{msg.senderName[0]}
{msg.type === "image" ? ( 聊天图片 ) : (

{msg.content}

)}

{new Date(msg.createdAt).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", })}

) })}
{!session.readonly ? (
{ const file = event.target.files?.[0] if (!file) return const result = sendImageMessage(session.id, URL.createObjectURL(file)) if (result && !result.ok) notifyInfo(result.message ?? "发送失败") event.target.value = "" }} />
{ e.preventDefault() const text = input.trim() if (!text) return const result = sendTextMessage(session.id, text) if (result && !result.ok) { notifyInfo(result.message ?? "发送失败") return } setInput("") }} > setInput(e.target.value)} placeholder="输入消息..." className="flex-1" />
) : (
订单已关闭,会话为只读状态
)}
) }