Compare commits

...

1 Commits

Author SHA1 Message Date
Innei 47602d8c40 fix: clear stale agent topic on bare route 2026-04-27 01:09:35 +08:00
2 changed files with 44 additions and 4 deletions
@@ -15,6 +15,20 @@ const useLocationMock = vi.hoisted(() => vi.fn());
const useParamsMock = vi.hoisted(() => vi.fn());
const useSearchParamsMock = vi.hoisted(() => vi.fn());
vi.hoisted(() => {
const storage = {
clear: vi.fn(),
getItem: vi.fn(() => null),
removeItem: vi.fn(),
setItem: vi.fn(),
};
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: storage,
});
});
vi.mock('react-router-dom', async () => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const actual = (await vi.importActual('react-router-dom')) as typeof import('react-router-dom');
@@ -94,6 +108,32 @@ describe('ChatHydration', () => {
});
});
it('clears stale topic and thread state when the route has no topic or thread', async () => {
useChatStore.setState(
{
activeThreadId: 'thd_previous',
activeTopicId: 'tpc_previous',
},
false,
);
useParamsMock.mockReturnValue({ aid: 'agt_next' });
useLocationMock.mockReturnValue({
hash: '',
pathname: '/agent/agt_next',
search: '',
});
useSearchParamsMock.mockReturnValue([new URLSearchParams(''), setSearchParamsMock]);
render(<ChatHydration />);
await waitFor(() => {
expect(useChatStore.getState().activeTopicId).toBeNull();
expect(useChatStore.getState().activeThreadId).toBeNull();
expect(navigateMock).not.toHaveBeenCalled();
});
});
it('rewrites the pathname when the active topic changes in the chat store', async () => {
useParamsMock.mockReturnValue({ aid: 'agt_test', topicId: 'tpc_123' });
useLocationMock.mockReturnValue({
@@ -2,11 +2,11 @@
import { memo, useLayoutEffect, useRef } from 'react';
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { createStoreUpdater } from 'zustand-utils';
import { SESSION_CHAT_TOPIC_URL, SESSION_CHAT_URL } from '@/const/url';
import { useQueryState } from '@/hooks/useQueryParam';
import { useChatStore } from '@/store/chat';
import { createStoreUpdater } from '@/store/utils/createStoreUpdater';
const getSearchSuffix = (searchParams: URLSearchParams) => {
const search = searchParams.toString();
@@ -25,8 +25,8 @@ const ChatHydration = memo(() => {
const [thread, setThread] = useQueryState('thread', { history: 'replace', throttleMs: 500 });
const routeTopicId = params.topicId ?? searchParams.get('topic');
useStoreUpdater('activeTopicId', routeTopicId ?? undefined);
useStoreUpdater('activeThreadId', thread!);
useStoreUpdater('activeTopicId', routeTopicId ?? null);
useStoreUpdater('activeThreadId', thread ?? null);
const locationRef = useRef(location);
const paramsRef = useRef(params);
@@ -84,7 +84,7 @@ const ChatHydration = memo(() => {
const unsubscribeThread = useChatStore.subscribe(
(s) => s.activeThreadId,
(state) => {
setThread(!state ? null : state);
setThread(state || null);
},
);