feat(chat): add image messages and enforce readonly sessions

This commit is contained in:
zetaloop
2026-02-22 08:17:09 +08:00
parent 5542015abe
commit ea822aaa8d
2 changed files with 87 additions and 10 deletions
+53 -10
View File
@@ -1,8 +1,9 @@
"use client"
import { ArrowLeft, Lock, Send } from "lucide-react"
import { ArrowLeft, ImagePlus, Lock, Send } from "lucide-react"
import Image from "next/image"
import Link from "next/link"
import { use, useState } from "react"
import { use, useRef, useState } from "react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
@@ -17,7 +18,9 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
const session = useChatStore((state) => state.sessions.find((item) => item.id === id))
const messages = useChatStore((state) => state.messages.filter((item) => item.sessionId === id))
const sendTextMessage = useChatStore((state) => state.sendTextMessage)
const sendImageMessage = useChatStore((state) => state.sendImageMessage)
const [input, setInput] = useState("")
const imageInputRef = useRef<HTMLInputElement>(null)
const { user } = useAuthStore()
if (!session) {
@@ -77,14 +80,25 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
<AvatarFallback>{msg.senderName[0]}</AvatarFallback>
</Avatar>
<div className={cn("max-w-[70%]", isMine && "text-right")}>
<p
className={cn(
"inline-block rounded-lg px-3 py-2 text-sm",
isMine ? "bg-primary text-primary-foreground" : "bg-muted",
)}
>
{msg.content}
</p>
{msg.type === "image" ? (
<Image
src={msg.content}
alt="聊天图片"
width={256}
height={192}
unoptimized
className="inline-block rounded-lg max-h-48 max-w-64 border"
/>
) : (
<p
className={cn(
"inline-block rounded-lg px-3 py-2 text-sm",
isMine ? "bg-primary text-primary-foreground" : "bg-muted",
)}
>
{msg.content}
</p>
)}
<p className="text-[10px] text-muted-foreground mt-1">
{new Date(msg.createdAt).toLocaleTimeString("zh-CN", {
hour: "2-digit",
@@ -100,6 +114,27 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
{!session.readonly ? (
<div className="border-t p-4">
<input
ref={imageInputRef}
type="file"
accept="image/*"
className="hidden"
onChange={(event) => {
const file = event.target.files?.[0]
if (!file) return
const sender = session.participants.find((participant) => participant.id === userId)
sendImageMessage(
session.id,
{
id: userId,
name: sender?.name ?? user?.nickname ?? "",
avatar: sender?.avatar ?? user?.avatar ?? "",
},
URL.createObjectURL(file),
)
event.target.value = ""
}}
/>
<form
className="flex gap-2 max-w-2xl mx-auto"
onSubmit={(e) => {
@@ -126,6 +161,14 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
placeholder="输入消息..."
className="flex-1"
/>
<Button
type="button"
size="icon"
variant="outline"
onClick={() => imageInputRef.current?.click()}
>
<ImagePlus className="h-4 w-4" />
</Button>
<Button type="submit" size="icon" disabled={!input.trim()}>
<Send className="h-4 w-4" />
</Button>