mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(brain): reload memory graph after re-login (#4223)
This commit is contained in:
+15
-1
@@ -26,6 +26,7 @@ import MemoryDebugPanel from '../components/settings/panels/MemoryDebugPanel';
|
||||
import BetaBanner from '../components/ui/BetaBanner';
|
||||
import { useSubconscious } from '../hooks/useSubconscious';
|
||||
import { useT } from '../lib/i18n/I18nContext';
|
||||
import { useCoreState } from '../providers/CoreStateProvider';
|
||||
import type { ToastNotification } from '../types/intelligence';
|
||||
import {
|
||||
type GraphExportResponse,
|
||||
@@ -96,6 +97,16 @@ export default function Brain() {
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
const [toasts, setToasts] = useState<ToastNotification[]>([]);
|
||||
|
||||
// The memory graph is read from the on-disk store, but the read only fired on
|
||||
// mount — so after a logout→login cycle the page kept whatever (empty) state
|
||||
// it had when the core was signed-out / mid identity-flip and never refetched
|
||||
// once auth was restored, showing an empty graph for an account whose data is
|
||||
// still on disk (#4149). Key the load on the authenticated identity so a
|
||||
// re-auth (null→user, or A→B) re-pulls the persisted graph, mirroring the
|
||||
// thread-cache reload CoreStateProvider already does on identity change.
|
||||
const { snapshot } = useCoreState();
|
||||
const authUserId = snapshot.auth.userId;
|
||||
|
||||
const sub = useSubconscious();
|
||||
|
||||
const addToast = useCallback((toast: Omit<ToastNotification, 'id'>) => {
|
||||
@@ -136,7 +147,10 @@ export default function Brain() {
|
||||
cancelled = true;
|
||||
window.removeEventListener('openhuman:memory-tree-completed', onTreeDone);
|
||||
};
|
||||
}, [mode, refreshKey]);
|
||||
// `authUserId` is a dependency so a logout→login (identity becomes
|
||||
// available again) re-pulls the persisted graph instead of leaving the
|
||||
// signed-out empty state on screen (#4149).
|
||||
}, [mode, refreshKey, authUserId]);
|
||||
|
||||
const cardClass = 'rounded-lg border border-line bg-surface p-4';
|
||||
|
||||
|
||||
@@ -5,12 +5,23 @@ import { renderWithProviders } from '../../test/test-utils';
|
||||
import Brain from '../Brain';
|
||||
|
||||
const graphExportMock = vi.hoisted(() => vi.fn());
|
||||
// Controllable authenticated identity so we can simulate a logout→login cycle
|
||||
// (userId null → set) and assert the graph reloads (#4149).
|
||||
const coreAuthRef = vi.hoisted(() => ({ current: 'user-A' as string | null }));
|
||||
|
||||
vi.mock('../../utils/tauriCommands', () => ({
|
||||
memoryTreeGraphExport: graphExportMock,
|
||||
isTauri: () => false,
|
||||
}));
|
||||
|
||||
vi.mock('../../providers/CoreStateProvider', () => ({
|
||||
useCoreState: () => ({
|
||||
snapshot: {
|
||||
auth: { userId: coreAuthRef.current, isAuthenticated: coreAuthRef.current != null },
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../components/intelligence/MemoryGraph', async () => {
|
||||
const React = await import('react');
|
||||
return {
|
||||
@@ -94,6 +105,7 @@ const makeGraph = (n: number) => ({
|
||||
describe('Brain page', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
coreAuthRef.current = 'user-A';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -120,6 +132,30 @@ describe('Brain page', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('reloads the memory graph from the store when the user re-authenticates (#4149)', async () => {
|
||||
// Start signed-out / mid identity-flip: the first fetch resolves empty.
|
||||
coreAuthRef.current = null;
|
||||
graphExportMock.mockResolvedValue(makeGraph(0));
|
||||
let view!: ReturnType<typeof renderWithProviders>;
|
||||
await act(async () => {
|
||||
view = renderWithProviders(<Brain />);
|
||||
});
|
||||
await waitFor(() => expect(graphExportMock).toHaveBeenCalledTimes(1));
|
||||
expect(screen.getByTestId('memory-graph')).toHaveTextContent('nodes:0');
|
||||
|
||||
// Re-login: identity becomes available — the graph must re-pull from the
|
||||
// persistent store rather than keep the signed-out empty state.
|
||||
coreAuthRef.current = 'user-A';
|
||||
graphExportMock.mockResolvedValue(makeGraph(5));
|
||||
await act(async () => {
|
||||
view.rerender(<Brain />);
|
||||
});
|
||||
await waitFor(() => expect(graphExportMock).toHaveBeenCalledTimes(2));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('memory-graph')).toHaveTextContent('nodes:5');
|
||||
});
|
||||
});
|
||||
|
||||
it('surfaces an error alert when the fetch fails', async () => {
|
||||
graphExportMock.mockRejectedValue(new Error('boom'));
|
||||
await act(async () => {
|
||||
|
||||
Reference in New Issue
Block a user