feat(web): add account workspaces and signed content flows
This commit is contained in:
1 parent
7b8d61d1ba
commit
b5cf3d5958
117 files changed
+2789
-1674
No files matched your search
@@ -1,5 +1,8 @@
|
||||
import { useEffect, useRef, useState, type KeyboardEvent } from 'react';
|
||||
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';
|
||||
|
||||
@@ -11,6 +14,7 @@ export function ChatPane({ threadName, ids, enabled, onSend, onDelete, onOpenFil
|
||||
const [error, setError] = useState('');
|
||||
const messages = useRef<HTMLDivElement>(null);
|
||||
const fileInput = useRef<HTMLInputElement>(null);
|
||||
const byId = useWorkspace((s) => s.byId);
|
||||
const chronological = ids.slice().reverse();
|
||||
|
||||
useEffect(() => { messages.current?.scrollTo({ top: messages.current.scrollHeight }); }, [chronological.length]);
|
||||
@@ -34,7 +38,23 @@ export function ChatPane({ threadName, ids, enabled, onSend, onDelete, onOpenFil
|
||||
<div className={styles.head}><strong>{threadName}</strong><span>频道聊天</span></div>
|
||||
<div className={styles.messages} ref={messages} aria-live="polite">
|
||||
{chronological.length === 0 ? <p className={styles.empty}>在这里发送消息或文件,成员可以看到你的名称和头像。</p> : null}
|
||||
{chronological.map((id) => <ChatMessage key={id} id={id} onOpenFile={onOpenFile} onDelete={onDelete} />)}
|
||||
{(() => {
|
||||
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 <Fragment key={id}>
|
||||
{showDate ? <div className={styles.dateDivider} role="separator"><span>{formatDate(item.ts)}</span></div> : null}
|
||||
<ChatMessage id={id} compact={grouped} onOpenFile={onOpenFile} onDelete={onDelete} />
|
||||
</Fragment>;
|
||||
});
|
||||
})()}
|
||||
</div>
|
||||
<form className={styles.composer} onSubmit={(event) => { event.preventDefault(); void submit(); }}>
|
||||
{onFiles ? <><input ref={fileInput} type="file" multiple hidden onChange={(event) => { if (event.target.files?.length) onFiles([...event.target.files]); event.target.value = ''; }} />
|
||||
|
||||
Reference in new issue
Block a user