import { Fragment, useEffect, useRef, useState, type KeyboardEvent } from 'react'; import { Button } from '@/ui/Button'; import { useWorkspace } from '@/store/workspace'; import type { Item } from '@/core-bridge/types'; import { formatDate } from '@/lib/format'; import { ChatMessage } from './ChatMessage'; import styles from './ChatPane.module.css'; interface Props { threadName: string; ids: string[]; enabled: boolean; onSend: (text: string) => Promise; onDelete: (id: string) => void; onOpenFile: (id: string) => void; onFiles?: (files: File[]) => void } export function ChatPane({ threadName, ids, enabled, onSend, onDelete, onOpenFile, onFiles }: Props) { const [text, setText] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); const messages = useRef(null); const fileInput = useRef(null); const byId = useWorkspace((s) => s.byId); const chronological = ids.slice().reverse(); useEffect(() => { messages.current?.scrollTo({ top: messages.current.scrollHeight }); }, [chronological.length]); async function submit() { const value = text.trim(); if (!value || busy || !enabled) return; setBusy(true); setError(''); try { await onSend(value); setText(''); } catch (cause) { setError(cause instanceof Error ? cause.message : '发送失败'); } finally { setBusy(false); } } function keyDown(event: KeyboardEvent) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); void submit(); } } return (
{threadName}频道聊天
{chronological.length === 0 ?

在这里发送消息或文件,成员可以看到你的名称和头像。

: null} {(() => { let previous: Item | undefined; return chronological.map((id) => { const item = byId[id]; if (!item) return null; const dateKey = new Date(item.ts).toDateString(); const previousDate = previous ? new Date(previous.ts).toDateString() : undefined; const showDate = dateKey !== previousDate; const sameSender = previous && (previous.author?.username ?? previous.deviceId) === (item.author?.username ?? item.deviceId); const grouped = Boolean(sameSender && !showDate && item.ts - previous!.ts < 5 * 60_000); previous = item; return {showDate ?
{formatDate(item.ts)}
: null}
; }); })()}
{ event.preventDefault(); void submit(); }}> {onFiles ? <> { if (event.target.files?.length) onFiles([...event.target.files]); event.target.value = ''; }} />