Files
WebDrop/web/src/app/useInvitationNotices.ts
T
justme 0d31e3d058 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
2026-10-14 22:42:06 +08:00

32 lines
1.4 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { accountApi } from '@/core-bridge/account';
import { acceptInvitation, invitationLabel } from '@/store/invitations';
import { useToast } from '@/store/toast';
const POLL_MS = 8000;
/** 登录后轮询账号邀请,出现新邀请就弹通知,可直接接受 */
export function useInvitationNotices(username: string | null): void {
const seen = useRef<Set<string> | null>(null);
useEffect(() => {
if (!username) { seen.current = null; return; }
let stopped = false;
async function poll() {
try {
const rows = await accountApi.invitations();
if (stopped) return;
const known = seen.current;
seen.current = new Set(rows.map((row) => row.channel_id));
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 acceptInvitation(row); } } });
}
} catch { /* 下一轮再试,不打扰用户 */ }
}
void poll();
const timer = window.setInterval(() => { void poll(); }, POLL_MS);
return () => { stopped = true; window.clearInterval(timer); };
}, [username]);
}