feat(web): discord-style chat, members pane, invitations and optimistic send [CI]
- chat: left-aligned discord-style messages with grouped senders, hover actions, an auto-growing composer and file cards - send: render messages and files locally first and replace the placeholder when the worker publishes the real item; failures keep the text with a retry action - navigation: space rail plus a flatter channel list with the space name header and collapsible groups - members: right pane tabs (files / members) grouped by online, offline and pending users, with a prominent invite action and a top bar shortcut - notifications: toast host for invite results; invitees get a toast with an accept action, and the invitation payload now carries the space name and inviter inside the encrypted grant - users over devices: drop device names, the device list and device rename/release; senders show the account (guests show as guest) and the share panel only keeps links - follow contract 7.0.0 (max_members, members_used, members_full, /join) - docs: ui-design and frontend updated for the new layout and concepts
This commit is contained in:
1 parent
5fd4ddd411
commit
4b8d0b5a5f
83 files changed
+1291
-661
No files matched your search
@@ -1,7 +1,8 @@
|
||||
import { formatSize, formatTime, formatTransferProgress } from '@/lib/format';
|
||||
import { useDownload } from '@/features/item/download';
|
||||
import { InlineContent, inlineKindOf } from '@/features/item/InlineContent';
|
||||
import { shortDevice, titleOf } from '@/features/item/summary';
|
||||
import { senderLabel } from '@/lib/sender';
|
||||
import { titleOf } from '@/features/item/summary';
|
||||
import { Tile } from '@/features/item/Tile';
|
||||
import { useWorkspace } from '@/store/workspace';
|
||||
import { Button } from '@/ui/Button';
|
||||
@@ -16,7 +17,6 @@ interface Props {
|
||||
/** 手机首屏:最新一项放大,拇指区一键取走 */
|
||||
export function LatestCard({ id, onOpen }: Props) {
|
||||
const item = useWorkspace((s) => s.byId[id]);
|
||||
const device = useWorkspace((s) => s.devices.find((d) => d.id === s.byId[id]?.deviceId)?.name);
|
||||
const download = useDownload();
|
||||
if (!item) return null;
|
||||
|
||||
@@ -32,7 +32,7 @@ export function LatestCard({ id, onOpen }: Props) {
|
||||
<span className={styles.text}>
|
||||
{inline === 'text' ? null : <span className={styles.title}>{titleOf(item)}</span>}
|
||||
<span className={styles.sub}>
|
||||
{shortDevice(device)} · {formatTime(item.ts)} · {formatSize(size)}
|
||||
{senderLabel(item)} · {formatTime(item.ts)} · {formatSize(size)}
|
||||
{receiving !== null && item.state.kind === 'receiving' ? ` · ${formatTransferProgress(item.state)}` : ''}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
Reference in new issue
Block a user