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 // Load personas and pick the default one as the web main-session persona. This
// persona. On failure currentPersona stays null and components fall back to the // must not hinge on a single mount-time fetch: a token may appear (login) after
// neutral default placeholder (never another role's avatar). // 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(() => { useEffect(() => {
if (!getAuthToken()) return; if (!token) return;
let cancelled = false; let cancelled = false;
fetchPersonas() let retry: number | null = null;
.then((list) => {
if (cancelled) return; const load = () => {
setCurrentPersona(list.find((p) => p.isDefault) ?? list[0] ?? null); if (cancelled) return;
}) fetchPersonas()
.catch(() => { .then((list) => {
// Keep currentPersona null → neutral default placeholder. 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 () => { return () => {
cancelled = true; 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. // 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. // `/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, mood: s.mood,
context: s.context, context: s.context,
backthink: s.backthink, 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,
}), }),
} }
) )