fix(web): currentPersona 稳定加载——不再因 mount 时无 token/一次失败就退回默认头像
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- useAppStore.partialize 持久化 currentPersona:刷新后保持本角色头像/背景
- App.tsx:token 出现即重拉 + 失败 3s 重试,替代一次性 mount 拉取
- 红线:仍只显示当前角色自己的头像/背景,绝不 fallback 其它角色
This commit is contained in:
Aska committed 2026-09-07 03:03:48 +08:00
1 parent ba8ff835cd
commit c42aae7797
2 files changed
+31 -13

No files matched your search

+27 -13
View File
@@ -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.
+4
View File
@@ -256,6 +256,10 @@ export const useAppStore = create<AppStore>()(
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,
}),
}
)