feat(chat): migrate chat to backend API
This commit is contained in:
@@ -6,32 +6,57 @@ 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 { getChatSessionById, listChatMessages } from "@/lib/api"
|
||||
import { sendImageMessage, sendTextMessage } from "@/lib/api/chat"
|
||||
import { notifyInfo } from "@/lib/toast"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useChatStore } from "@/store/chat"
|
||||
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 { use, useEffect, useRef, useState } from "react"
|
||||
|
||||
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 [loading, setLoading] = useState(true)
|
||||
const [session, setSession] = useState<
|
||||
Awaited<ReturnType<typeof getChatSessionById>> | undefined
|
||||
>(undefined)
|
||||
const [messages, setMessages] = useState<Awaited<ReturnType<typeof listChatMessages>>>([])
|
||||
const [input, setInput] = useState("")
|
||||
const imageInputRef = useRef<HTMLInputElement>(null)
|
||||
const { user } = useAuthStore()
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
Promise.all([Promise.resolve(getChatSessionById(id)), Promise.resolve(listChatMessages(id))])
|
||||
.then(([nextSession, nextMessages]) => {
|
||||
if (cancelled) return
|
||||
setSession(nextSession)
|
||||
setMessages(nextMessages)
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return
|
||||
setSession(undefined)
|
||||
setMessages([])
|
||||
})
|
||||
.finally(() => {
|
||||
if (cancelled) return
|
||||
setLoading(false)
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [id])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">加载中...</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
|
||||
@@ -147,11 +172,22 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0]
|
||||
const target = event.currentTarget
|
||||
const file = target.files?.[0]
|
||||
if (!file) return
|
||||
const result = sendImageMessage(session.id, URL.createObjectURL(file))
|
||||
if (!result.ok) notifyInfo(result.error.msg)
|
||||
event.target.value = ""
|
||||
const imageUrl = URL.createObjectURL(file)
|
||||
|
||||
Promise.resolve(sendImageMessage(session.id, imageUrl))
|
||||
.then((result) => {
|
||||
if (!result.ok) {
|
||||
notifyInfo(result.error.msg)
|
||||
return
|
||||
}
|
||||
return Promise.resolve(listChatMessages(session.id)).then(setMessages)
|
||||
})
|
||||
.finally(() => {
|
||||
target.value = ""
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<form
|
||||
@@ -161,12 +197,14 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
|
||||
const text = input.trim()
|
||||
if (!text) return
|
||||
|
||||
const result = sendTextMessage(session.id, text)
|
||||
if (!result.ok) {
|
||||
notifyInfo(result.error.msg)
|
||||
return
|
||||
}
|
||||
setInput("")
|
||||
Promise.resolve(sendTextMessage(session.id, text)).then((result) => {
|
||||
if (!result.ok) {
|
||||
notifyInfo(result.error.msg)
|
||||
return
|
||||
}
|
||||
setInput("")
|
||||
return Promise.resolve(listChatMessages(session.id)).then(setMessages)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
|
||||
Reference in New Issue
Block a user