import { useState, type FormEvent } from 'react'; import type { ChannelGroupInfo, ThreadInfo } from '@/core-bridge/types'; import { Button } from '@/ui/Button'; import { Icon } from '@/ui/Icon'; import { Sheet } from '@/ui/Sheet'; import styles from './ThreadSidebar.module.css'; interface Props { threads: ThreadInfo[]; groups: ChannelGroupInfo[]; selectedThreadId?: string; enabled: boolean; owner: boolean; onHistory: (id: string, chatHistory: boolean, filesHistory: boolean) => Promise; onSelectThread: (id: string) => void; onCreateThread: (name: string, groupId?: string) => Promise; onRenameThread: (id: string, name: string) => Promise; onCreateGroup: (name: string) => Promise; onRenameGroup: (id: string, name: string) => Promise; onDeleteGroup: (id: string) => Promise; onGroupThread: (id: string, groupId?: string) => Promise; } type Editor = { kind: 'channel' | 'group'; id?: string; groupId?: string }; const UNGROUPED = '__ungrouped__'; export function ThreadSidebar(props: Props) { const [edit, setEdit] = useState(); const [name, setName] = useState(''); const [groupId, setGroupId] = useState(''); const [chatHistory, setChatHistory] = useState(true); const [filesHistory, setFilesHistory] = useState(true); const [busy, setBusy] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [error, setError] = useState(''); const [collapsed, setCollapsed] = useState>(new Set()); const knownGroups = new Set(props.groups.map((group) => group.id)); const ungrouped = props.threads.filter((thread) => !thread.groupId || !knownGroups.has(thread.groupId)); function begin(target: Editor, initial = '') { setEdit(target); setName(initial); setGroupId(target.groupId ?? ''); setError(''); setConfirmDelete(false); const channel = props.threads.find(t => t.id === target.id); setChatHistory(channel?.chatHistory ?? true); setFilesHistory(channel?.filesHistory ?? true); } function toggle(id: string) { setCollapsed((previous) => { const next = new Set(previous); if (next.has(id)) next.delete(id); else next.add(id); return next; }); } function select(id: string) { props.onSelectThread(id); setEdit(undefined); } async function removeGroup() { if (!edit?.id || busy || !props.enabled) return; setBusy(true); setError(''); try { await props.onDeleteGroup(edit.id); setEdit(undefined); } catch (cause) { setError(cause instanceof Error ? cause.message : '删除失败'); } finally { setBusy(false); } } async function submit(event: FormEvent) { event.preventDefault(); if (!edit || !name.trim() || busy || !props.enabled) return; setBusy(true); setError(''); try { if (edit.kind === 'group') { if (edit.id) await props.onRenameGroup(edit.id, name); else await props.onCreateGroup(name); } else if (edit.id) { const original = props.threads.find((thread) => thread.id === edit.id); if (original?.name !== name.trim()) await props.onRenameThread(edit.id, name); if ((original?.groupId ?? '') !== groupId) await props.onGroupThread(edit.id, groupId || undefined); if (props.owner && (original?.chatHistory !== chatHistory || original?.filesHistory !== filesHistory)) await props.onHistory(edit.id, chatHistory, filesHistory); } else props.onSelectThread(await props.onCreateThread(name, groupId || undefined)); if (groupId) setCollapsed((previous) => { const next = new Set(previous); next.delete(groupId); return next; }); setEdit(undefined); } catch (cause) { setError(cause instanceof Error ? cause.message : '操作失败'); } finally { setBusy(false); } } return
频道
{(ungrouped.length > 0 || props.groups.length === 0) ?
{props.enabled ?
{!collapsed.has(UNGROUPED) ? begin({ kind: 'channel', id: thread.id, groupId: thread.groupId }, thread.name)} /> : null}
: null} {props.groups.map((group) => { const threads = props.threads.filter((thread) => thread.groupId === group.id); return
{props.enabled ?
{!collapsed.has(group.id) ? begin({ kind: 'channel', id: thread.id, groupId: thread.groupId }, thread.name)} /> : null} {!collapsed.has(group.id) && !threads.length ?

此分组还没有频道。

: null}
; })} {edit ? { if (!busy) setEdit(undefined); }}>
{ void submit(event); }}> {edit.kind === 'channel' ? : null} {edit.kind === 'channel' && edit.id && props.owner ? <> : null} {edit.kind === 'group' && edit.id ?
{confirmDelete ? <>

分组会被删除,其中的频道回到「未分组」,频道和消息都不会丢。

: }
: null} {error ?

{error}

: null}
: null} {!props.threads.length && !props.groups.length ?

创建一个频道后开始聊天和上传文件。

: null} {!edit && error ?

{error}

: null}
; } function ChannelRows({ threads, selectedId, enabled, busy, nested = false, onSelect, onEdit }: { threads: ThreadInfo[]; selectedId?: string; enabled: boolean; busy: boolean; nested?: boolean; onSelect: (id: string) => void; onEdit: (thread: ThreadInfo) => void; }) { return
    {threads.map((thread) =>
  • {enabled ?
  • )}
; }