From c42aae779784ed6f07f2bce2e7a25feaf4005414 Mon Sep 17 00:00:00 2001 From: Aska Date: Mon, 7 Sep 2026 03:03:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20currentPersona=20=E7=A8=B3=E5=AE=9A?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E2=80=94=E2=80=94=E4=B8=8D=E5=86=8D=E5=9B=A0?= =?UTF-8?q?=20mount=20=E6=97=B6=E6=97=A0=20token/=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E5=B0=B1=E9=80=80=E5=9B=9E=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useAppStore.partialize 持久化 currentPersona:刷新后保持本角色头像/背景 - App.tsx:token 出现即重拉 + 失败 3s 重试,替代一次性 mount 拉取 - 红线:仍只显示当前角色自己的头像/背景,绝不 fallback 其它角色 --- frontend/web/src/App.tsx | 40 ++++++++++++++++++--------- frontend/web/src/store/useAppStore.ts | 4 +++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/frontend/web/src/App.tsx b/frontend/web/src/App.tsx index ae3ef03..a0f35e9 100644 --- a/frontend/web/src/App.tsx +++ b/frontend/web/src/App.tsx @@ -26,24 +26,38 @@ export default function App(): React.ReactNode { }; }, []); - // Load personas once on mount and pick the default one as the web main-session - // persona. On failure currentPersona stays null and components fall back to the - // neutral default placeholder (never another role's avatar). + // Load personas and pick the default one as the web main-session persona. This + // must not hinge on a single mount-time fetch: a token may appear (login) after + // mount, or the fetch may transiently fail — in which case currentPersona would + // stay null and the UI would show a neutral placeholder (or worse, silently + // persist a null). We re-run whenever the token appears, and retry on failure. + // The resolved persona is also persisted by useAppStore so a refresh keeps the + // correct avatar/background. + const token = getAuthToken(); useEffect(() => { - if (!getAuthToken()) return; + if (!token) return; let cancelled = false; - fetchPersonas() - .then((list) => { - if (cancelled) return; - setCurrentPersona(list.find((p) => p.isDefault) ?? list[0] ?? null); - }) - .catch(() => { - // Keep currentPersona null → neutral default placeholder. - }); + let retry: number | null = null; + + const load = () => { + if (cancelled) return; + fetchPersonas() + .then((list) => { + if (cancelled) return; + setCurrentPersona(list.find((p) => p.isDefault) ?? list[0] ?? null); + }) + .catch(() => { + // Transient failure — retry rather than leaving the placeholder forever. + if (!cancelled) retry = window.setTimeout(load, 3000); + }); + }; + load(); + return () => { cancelled = true; + if (retry !== null) window.clearTimeout(retry); }; - }, [setCurrentPersona]); + }, [token, setCurrentPersona]); // The M9 modal UI and the admin subsystem (design/10) are two separate shells. // `/admin` is reached via `#/admin`; every admin module is a hash segment. diff --git a/frontend/web/src/store/useAppStore.ts b/frontend/web/src/store/useAppStore.ts index 7a0aace..3515953 100644 --- a/frontend/web/src/store/useAppStore.ts +++ b/frontend/web/src/store/useAppStore.ts @@ -256,6 +256,10 @@ export const useAppStore = create()( mood: s.mood, context: s.context, backthink: s.backthink, + // Persist the resolved persona so a refresh keeps showing THIS persona's + // own avatar/background — never falling back to a neutral placeholder or + // (worse) another role's image because the mount-time fetch missed. + currentPersona: s.currentPersona, }), } )