feat(web): tidy the channel sidebar and let owners delete groups
This commit is contained in:
1 parent
6310f93469
commit
6d6490055e
12 files changed
+66
-13
No files matched your search
@@ -17,6 +17,7 @@ interface Props {
|
||||
onRenameThread: (id: string, name: string) => Promise<void>;
|
||||
onCreateGroup: (name: string) => Promise<string>;
|
||||
onRenameGroup: (id: string, name: string) => Promise<void>;
|
||||
onDeleteGroup: (id: string) => Promise<void>;
|
||||
onGroupThread: (id: string, groupId?: string) => Promise<void>;
|
||||
}
|
||||
type Editor = { kind: 'channel' | 'group'; id?: string; groupId?: string };
|
||||
@@ -29,16 +30,24 @@ export function ThreadSidebar(props: Props) {
|
||||
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<ReadonlySet<string>>(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('');
|
||||
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('');
|
||||
@@ -57,7 +66,7 @@ export function ThreadSidebar(props: Props) {
|
||||
finally { setBusy(false); }
|
||||
}
|
||||
return <section className={styles.panel} aria-label="空间频道导航">
|
||||
<div className={styles.heading}><strong>频道</strong><Button variant="ghost" icon="folder" label="新建频道分组" disabled={!props.enabled || busy} onClick={() => begin({ kind: 'group' })} /></div>
|
||||
<div className={styles.heading}><strong>频道</strong><Button variant="ghost" icon="plus" label="新建频道分组" disabled={!props.enabled || busy} onClick={() => begin({ kind: 'group' })} /></div>
|
||||
{(ungrouped.length > 0 || props.groups.length === 0) ? <section className={styles.group} aria-label="未分组频道">
|
||||
<div className={styles.groupHeading}>
|
||||
<button className={styles.groupToggle} type="button" aria-label="未分组频道" aria-expanded={!collapsed.has(UNGROUPED)} onClick={() => toggle(UNGROUPED)}>
|
||||
@@ -94,6 +103,15 @@ export function ThreadSidebar(props: Props) {
|
||||
<label className={styles.check}><input type="checkbox" checked={chatHistory} onChange={event => setChatHistory(event.target.checked)} />新成员可读历史聊天</label>
|
||||
<label className={styles.check}><input type="checkbox" checked={filesHistory} onChange={event => setFilesHistory(event.target.checked)} />新成员可读历史文件</label>
|
||||
</> : null}
|
||||
{edit.kind === 'group' && edit.id ? <div className={styles.dangerZone}>
|
||||
{confirmDelete ? <>
|
||||
<p className={styles.hint}>分组会被删除,其中的频道回到「未分组」,频道和消息都不会丢。</p>
|
||||
<div className={styles.groupActions}>
|
||||
<Button variant="ghost" disabled={busy} onClick={() => setConfirmDelete(false)}>先不删</Button>
|
||||
<Button variant="danger" disabled={busy} onClick={() => { void removeGroup(); }}>确认删除分组</Button>
|
||||
</div>
|
||||
</> : <Button variant="ghost" icon="trash" disabled={busy} onClick={() => setConfirmDelete(true)}>删除分组</Button>}
|
||||
</div> : null}
|
||||
{error ? <p className={styles.error} role="alert">{error}</p> : null}
|
||||
<div className={`${styles.groupActions} ${styles.modalActions}`}>
|
||||
<Button variant="ghost" disabled={busy} onClick={() => setEdit(undefined)}>取消</Button>
|
||||
|
||||
Reference in new issue
Block a user