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, }), } )