feat(web): redesign account settings and the sign-in page
- account settings: sidebar sections (profile, devices, security, invitations, sync and sign-in) instead of one long scroll; inline device rename, confirmations for destructive actions and toast feedback - sign-in/register: split layout with a quiet brand column, text tabs and underlined inputs; registering asks for the password twice and explains that it cannot be recovered - move invitation accept/label helpers into store/invitations so the toast and the settings inbox share them - add the home page zone styles used by the next commit
This commit is contained in:
1 parent
4b8d0b5a5f
commit
0d31e3d058
16 files changed
+882
-454
No files matched your search
@@ -1,27 +1,10 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { accountApi, type AccountInvitation } from '@/core-bridge/account';
|
||||
import { errorMessage } from '@/core-bridge/errors';
|
||||
import { useChannels } from '@/store/channels';
|
||||
import { accountApi } from '@/core-bridge/account';
|
||||
import { acceptInvitation, invitationLabel } from '@/store/invitations';
|
||||
import { useToast } from '@/store/toast';
|
||||
import { channelPath, navigate } from './navigation';
|
||||
|
||||
const POLL_MS = 8000;
|
||||
|
||||
export function invitationLabel(row: AccountInvitation): string {
|
||||
return row.channelName || `空间 ${row.channel_id.slice(0, 8)}…`;
|
||||
}
|
||||
|
||||
async function accept(row: AccountInvitation): Promise<void> {
|
||||
try {
|
||||
await accountApi.acceptInvitation(row.channel_id);
|
||||
await useChannels.getState().refresh();
|
||||
useToast.getState().push({ kind: 'ok', text: `已加入 ${invitationLabel(row)}` });
|
||||
navigate(channelPath(row.channel_id));
|
||||
} catch (cause) {
|
||||
useToast.getState().push({ kind: 'error', text: `加入失败:${errorMessage(cause)}` });
|
||||
}
|
||||
}
|
||||
|
||||
/** 登录后轮询账号邀请,出现新邀请就弹通知,可直接接受 */
|
||||
export function useInvitationNotices(username: string | null): void {
|
||||
const seen = useRef<Set<string> | null>(null);
|
||||
@@ -37,7 +20,7 @@ export function useInvitationNotices(username: string | null): void {
|
||||
const fresh = known ? rows.filter((row) => !known.has(row.channel_id)) : rows;
|
||||
for (const row of fresh) {
|
||||
useToast.getState().push({ kind: 'info', text: row.invitedBy ? `${row.invitedBy} 邀请你加入 ${invitationLabel(row)}` : `你收到了加入 ${invitationLabel(row)} 的邀请`,
|
||||
action: { label: '接受', run: () => { void accept(row); } } });
|
||||
action: { label: '接受', run: () => { void acceptInvitation(row); } } });
|
||||
}
|
||||
} catch { /* 下一轮再试,不打扰用户 */ }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user