diff --git a/apps/server/src/api/v2/chats/services/chat-helpers.test.ts b/apps/server/src/api/v2/chats/services/chat-helpers.test.ts index 8da48782a..fcb54dd31 100644 --- a/apps/server/src/api/v2/chats/services/chat-helpers.test.ts +++ b/apps/server/src/api/v2/chats/services/chat-helpers.test.ts @@ -213,7 +213,7 @@ describe('handleExistingChat', () => { it('should prepend new message to maintain descending order (newest first)', async () => { const baseTime = new Date('2024-01-01T10:00:00Z'); - + const mockChat = { id: 'chat-1', title: 'Test Chat', @@ -292,35 +292,40 @@ describe('handleExistingChat', () => { // getMessagesForChat returns existing messages in descending order (newest first) vi.mocked(database.getMessagesForChat).mockResolvedValue([existingMessage1, existingMessage2]); - const result = await handleExistingChat('chat-1', 'message-3', 'Third message (newest)', mockUser); + const result = await handleExistingChat( + 'chat-1', + 'message-3', + 'Third message (newest)', + mockUser + ); expect(result.chat.messages['message-3']).toBeDefined(); expect(result.chat.messages['message-1']).toBeDefined(); expect(result.chat.messages['message-2']).toBeDefined(); expect(result.chat.message_ids).toEqual(['message-3', 'message-1', 'message-2']); - + expect(result.chat.message_ids[0]).toBe('message-3'); - + const message3 = result.chat.messages['message-3']; const message1 = result.chat.messages['message-1']; const message2 = result.chat.messages['message-2']; - + expect(message3).toBeDefined(); expect(message1).toBeDefined(); expect(message2).toBeDefined(); - + const message3Time = new Date(message3!.created_at).getTime(); const message1Time = new Date(message1!.created_at).getTime(); const message2Time = new Date(message2!.created_at).getTime(); - + expect(message3Time).toBeGreaterThan(message1Time); expect(message1Time).toBeGreaterThan(message2Time); }); it('should handle single existing message with new message correctly', async () => { const baseTime = new Date('2024-01-01T10:00:00Z'); - + const mockChat = { id: 'chat-1', title: 'Test Chat', @@ -387,13 +392,13 @@ describe('handleExistingChat', () => { expect(result.chat.messages['message-2']).toBeDefined(); expect(result.chat.message_ids).toEqual(['message-2', 'message-1']); - + expect(result.chat.message_ids[0]).toBe('message-2'); }); it('should handle empty existing messages with new message', async () => { const baseTime = new Date('2024-01-01T10:00:00Z'); - + const mockChat = { id: 'chat-1', title: 'Test Chat', diff --git a/apps/web/src/api/buster_rest/chats/queryRequests.ts b/apps/web/src/api/buster_rest/chats/queryRequests.ts index 9ddaf2feb..2e5ab6520 100644 --- a/apps/web/src/api/buster_rest/chats/queryRequests.ts +++ b/apps/web/src/api/buster_rest/chats/queryRequests.ts @@ -214,7 +214,9 @@ export const useDeleteChat = () => { data: Parameters[0]; useConfirmModal?: boolean; }) => { - const method = () => deleteChat(data); + const method = async () => { + await deleteChat(data); + }; if (useConfirmModal) { return await openConfirmModal({ title: 'Delete Chat', @@ -228,7 +230,8 @@ export const useDeleteChat = () => { return useMutation({ mutationFn, - onSuccess() { + retry: false, + onSuccess: () => { queryClient.invalidateQueries({ queryKey: chatQueryKeys.chatsGetList().queryKey, refetchType: 'all' diff --git a/apps/web/src/context/BusterNotifications/useConfirmModal.ts b/apps/web/src/context/BusterNotifications/useConfirmModal.ts index faf3085fa..b7f285acf 100644 --- a/apps/web/src/context/BusterNotifications/useConfirmModal.ts +++ b/apps/web/src/context/BusterNotifications/useConfirmModal.ts @@ -3,6 +3,7 @@ import type { ConfirmProps as BaseConfirmProps, ConfirmModalProps } from '@/components/ui/modal/ConfirmModal'; +import { USER_CANCELLED_ERROR } from '../BusterReactQuery/queryClientConfig'; interface ConfirmProps extends Omit { title: string | React.ReactNode; @@ -15,7 +16,7 @@ const defaultConfirmModalProps: ConfirmProps = { title: '', content: '', onOk: () => undefined, - onCancel: async () => {} + onCancel: async () => Promise.reject(USER_CANCELLED_ERROR) }; interface QueuedModal extends Omit, 'onOk' | 'onCancel'> { diff --git a/apps/web/src/context/BusterReactQuery/BusterReactQueryAndApi.tsx b/apps/web/src/context/BusterReactQuery/BusterReactQueryAndApi.tsx index 6be95b15d..12cf80bee 100644 --- a/apps/web/src/context/BusterReactQuery/BusterReactQueryAndApi.tsx +++ b/apps/web/src/context/BusterReactQuery/BusterReactQueryAndApi.tsx @@ -57,12 +57,6 @@ export const BusterReactQueryProvider = ({ children }: { children: React.ReactNo ); }, []); - // const busterApiContext = useMemo(() => { - // return { - // honoInstance: createHonoInstance(BASE_API_URL_V2, checkTokenValidity) - // }; - // }, [checkTokenValidity]); - useHotkeys( 'meta+shift+i', (e) => { diff --git a/apps/web/src/context/BusterReactQuery/getQueryClient.ts b/apps/web/src/context/BusterReactQuery/getQueryClient.ts index 2f851ccd6..31d62a6fc 100644 --- a/apps/web/src/context/BusterReactQuery/getQueryClient.ts +++ b/apps/web/src/context/BusterReactQuery/getQueryClient.ts @@ -1,13 +1,15 @@ import { isServer, QueryClient } from '@tanstack/react-query'; import type { useBusterNotifications } from '../BusterNotifications'; import { openErrorNotification as openErrorNotificationMethod } from '../BusterNotifications'; +import { + ERROR_RETRY_DELAY, + GC_TIME, + PREFETCH_STALE_TIME, + USER_CANCELLED_ERROR +} from './queryClientConfig'; type OpenErrorNotification = ReturnType['openErrorNotification']; -const PREFETCH_STALE_TIME = 1000 * 10; // 10 seconds -const ERROR_RETRY_DELAY = 1 * 1000; // 1 second delay after error -const GC_TIME = 1000 * 60 * 60 * 24 * 3; // 24 hours - matches persistence duration - function makeQueryClient(params?: { openErrorNotification?: OpenErrorNotification; enabled?: boolean; @@ -34,7 +36,7 @@ function makeQueryClient(params?: { }, mutations: { retry: (failureCount, error) => { - if (params?.openErrorNotification) { + if (params?.openErrorNotification && error !== USER_CANCELLED_ERROR) { params.openErrorNotification(error); } return false; diff --git a/apps/web/src/context/BusterReactQuery/queryClientConfig.ts b/apps/web/src/context/BusterReactQuery/queryClientConfig.ts new file mode 100644 index 000000000..e6414ac07 --- /dev/null +++ b/apps/web/src/context/BusterReactQuery/queryClientConfig.ts @@ -0,0 +1,4 @@ +export const PREFETCH_STALE_TIME = 1000 * 10; // 10 seconds +export const ERROR_RETRY_DELAY = 1 * 1000; // 1 second delay after error +export const GC_TIME = 1000 * 60 * 60 * 24 * 3; // 24 hours - matches persistence duration +export const USER_CANCELLED_ERROR = new Error('User cancelled'); diff --git a/apps/web/src/controllers/CollectionIndividualController/CollectionIndividualHeader.tsx b/apps/web/src/controllers/CollectionIndividualController/CollectionIndividualHeader.tsx index 5aba88b4d..83b5a1fba 100644 --- a/apps/web/src/controllers/CollectionIndividualController/CollectionIndividualHeader.tsx +++ b/apps/web/src/controllers/CollectionIndividualController/CollectionIndividualHeader.tsx @@ -112,7 +112,14 @@ const ThreeDotDropdown: React.FC<{ icon: , onClick: async () => { try { - await deleteCollection({ id }); + await deleteCollection( + { id }, + { + onSuccess: () => { + onChangePage({ route: BusterRoutes.APP_COLLECTIONS }); + } + } + ); onChangePage({ route: BusterRoutes.APP_COLLECTIONS }); } catch (error) { //