fix(web): currentPersona 稳定加载——不再因 mount 时无 token/一次失败就退回默认头像
- useAppStore.partialize 持久化 currentPersona:刷新后保持本角色头像/背景 - App.tsx:token 出现即重拉 + 失败 3s 重试,替代一次性 mount 拉取 - 红线:仍只显示当前角色自己的头像/背景,绝不 fallback 其它角色
This commit is contained in:
1 parent
ba8ff835cd
commit
c42aae7797
2 files changed
+31
-13
No files matched your search
+27
-13
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in new issue
Block a user