diff --git a/client/electron/src/components/AccountSwitcher.tsx b/client/electron/src/components/AccountSwitcher.tsx index b5835cb..5742bd2 100644 --- a/client/electron/src/components/AccountSwitcher.tsx +++ b/client/electron/src/components/AccountSwitcher.tsx @@ -12,6 +12,7 @@ import { useEffect, useRef, useState } from 'react'; import { AGGREGATE_ID, isUsableAccount } from '../lib/accounts'; +import { resetAccountData } from '../lib/resetAccountData'; import { useAccountStore } from '../stores/accountStore'; import { useMailStore } from '../stores/mailStore'; import { useUIStore } from '../stores/uiStore'; @@ -55,9 +56,20 @@ export default function AccountSwitcher() { const pick = async (id: string) => { setOpen(false); + /* + * ★ 切账号**必须先清全部账号数据**,再重取。 + * + * `setActive` 会 `syncAuth()` 翻 `api/config` 单例里的 API_BASE 与 bearer, + * 从这一刻起 `api.getSent()` / `api.getMail()` / `api.archiveContact()` 全带 + * **新**账号的凭证。而 mailStore 的 sent/currentMail、sessionStore 的 + * sessions/currentSession(+Mails)、contactStore 的 contacts 全是**旧**账号的 —— + * 留着就会"在新账号的界面下显示旧账号的邮件",而从这些陈旧视图点归档/转发 + * 改的是**新**账号。不报错、不空屏,肉眼完全看不出来,所以只能靠这里堵。 + * + * 登出与 401 走的是同一套清空(`App.tsx` 与 `api/client.ts` 的 unauthorized 回调)。 + */ + resetAccountData(); await setActive(id); - // 切账号后**必须重取**:收件箱是按账号返回的,不重取就会看到上一个账号的信。 - // 这条在聚合⇄单账号之间同样成立(聚合要并发问多个账号)。 await fetchInbox('all'); }; diff --git a/client/electron/src/components/AdminUsersPage.tsx b/client/electron/src/components/AdminUsersPage.tsx index b54946e..358f7ba 100644 --- a/client/electron/src/components/AdminUsersPage.tsx +++ b/client/electron/src/components/AdminUsersPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import * as api from '../api/client'; import type { AdminScopes, User } from '../types'; import { CheckIcon, LockIcon, UsersIcon, ChevronRightIcon, KeyIcon, BotIcon, CpuIcon } from './icons'; @@ -89,9 +89,22 @@ export default function AdminUsersPage() { } }; + /* + * 提示条自动消失。句柄存进 ref 并在卸载时清掉:原来直接 `setTimeout` 不管, + * 组件卸载后那个回调仍会跑(状态写在死组件上是纯粹的脏行为),而且连点几次 + * 会留下多个待触发的定时器,先触发的会把后触发的提示提前吃掉。 + */ + const noticeTimer = useRef | null>(null); + useEffect(() => { + return () => { + if (noticeTimer.current) clearTimeout(noticeTimer.current); + }; + }, []); + const flash = (msg: string) => { setNotice(msg); - setTimeout(() => setNotice(null), 2500); + if (noticeTimer.current) clearTimeout(noticeTimer.current); + noticeTimer.current = setTimeout(() => setNotice(null), 2500); }; return ( diff --git a/client/electron/src/components/BackgroundPicker.tsx b/client/electron/src/components/BackgroundPicker.tsx index ca8a8fa..4e6a918 100644 --- a/client/electron/src/components/BackgroundPicker.tsx +++ b/client/electron/src/components/BackgroundPicker.tsx @@ -115,10 +115,17 @@ export default function BackgroundPicker() { presetId === p.id ? 'border-blue-500 ring-1 ring-blue-500' : 'border-gray-300' }`} > - {/* 用与全屏背景同一份 CSS 变量渲染缩略图 —— 预览必然等于结果 */} + {/* + * 用与全屏背景同一份 CSS 变量渲染缩略图 —— 预览必然等于结果。 + * + * ★ 这里**不能**写 `style={{ backgroundImage: 'var(--bg-image)' }}`: + * 内联样式优先于类,而当 `kind==='image'` 时 `:root` 上被设了 + * `--bg-image: url("用户那张图")`(`backgroundStore.ts:262`)⇒ + * **六个预设格子全都显示同一张照片**,而用户正在这一步里挑预设。 + * 预设类自己就定义了 `--bg-image`(`index.css:783+`),让它生效即可。 + */}