diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts index 4220fb9ce96..fd3697718b8 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts @@ -47,7 +47,11 @@ export const useFindDuplicateRecordsQuery = ({ } } `, - [objectMetadataItem, objectMetadataItems, objectPermissionsByObjectMetadataId], + [ + objectMetadataItem, + objectMetadataItems, + objectPermissionsByObjectMetadataId, + ], ); return { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindManyRecords.ts index f8f48d80dcd..5f82dfa8ff7 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindManyRecords.ts @@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; +import { type ErrorLike } from '@apollo/client'; import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; @@ -100,7 +101,28 @@ export const useLazyFindManyRecords = ({ }; } - const result = await findManyRecords({ variables: defaultVariables }); + // In Apollo v4, useLazyQuery's execute can reject (unlike v3 which + // resolved with the error in the result). We catch rejections here to + // prevent unhandled exceptions that would leave loading states stuck. + let result; + + try { + result = await findManyRecords({ variables: defaultVariables }); + } catch (executionError) { + handleFindManyRecordsError(executionError as ErrorLike); + + store.set(hasNextPageFamilyState.atomFamily(queryIdentifier), false); + store.set(cursorFamilyState.atomFamily(queryIdentifier), ''); + + return { + data: null, + records: [] as T[], + totalCount: 0, + hasNextPage: false, + error: executionError as ErrorLike, + }; + } + if (isDefined(result?.error)) { handleFindManyRecordsError(result.error); } diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts index 655fb4e7148..85620863743 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts @@ -121,85 +121,87 @@ export const useTriggerInitialRecordTableDataLoad = () => { store.set(isInitializingVirtualTableDataLoadingCallbackState, true); - resetTableFocuses(); + try { + resetTableFocuses(); - resetVirtualizedRowTreadmill(); + resetVirtualizedRowTreadmill(); - updateRecordTableCSSVariable( - RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME, - 'hidden', - ); - - updateRecordTableCSSVariable( - RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME, - 'hidden', - ); - - const currentRecordIds = store.get(recordIndexAllRecordIds); - - let records: ObjectRecord[] | null = null; - let totalCount = 0; - - if (showAuthModal) { - records = SIGN_IN_BACKGROUND_MOCK_COMPANIES; - totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length; - } else { - const newRecordIdByRealIndex = new Map( - store.get(recordIdByRealIndexCallbackState), - ); - const newDataLoadingStatusByRealIndex = new Map( - store.get(dataLoadingStatusByRealIndexCallbackState), + updateRecordTableCSSVariable( + RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME, + 'hidden', ); - for (const [realIndex] of currentRecordIds.entries()) { - newDataLoadingStatusByRealIndex.set(realIndex, 'not-loaded'); - newRecordIdByRealIndex.delete(realIndex); + updateRecordTableCSSVariable( + RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME, + 'hidden', + ); + + const currentRecordIds = store.get(recordIndexAllRecordIds); + + let records: ObjectRecord[] | null = null; + let totalCount = 0; + + if (showAuthModal) { + records = SIGN_IN_BACKGROUND_MOCK_COMPANIES; + totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length; + } else { + const newRecordIdByRealIndex = new Map( + store.get(recordIdByRealIndexCallbackState), + ); + const newDataLoadingStatusByRealIndex = new Map( + store.get(dataLoadingStatusByRealIndexCallbackState), + ); + + for (const [realIndex] of currentRecordIds.entries()) { + newDataLoadingStatusByRealIndex.set(realIndex, 'not-loaded'); + newRecordIdByRealIndex.delete(realIndex); + } + + store.set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex); + store.set( + dataLoadingStatusByRealIndexCallbackState, + newDataLoadingStatusByRealIndex, + ); + + const { records: findManyRecords, totalCount: findManyTotalCount } = + await findManyRecordsLazy(); + + records = findManyRecords; + totalCount = findManyTotalCount; } - store.set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex); - store.set( - dataLoadingStatusByRealIndexCallbackState, - newDataLoadingStatusByRealIndex, - ); + store.set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount); - const { records: findManyRecords, totalCount: findManyTotalCount } = - await findManyRecordsLazy(); + if (isDefined(records)) { + upsertRecordsInStore({ partialRecords: records }); - records = findManyRecords; - totalCount = findManyTotalCount; - } + loadRecordsToVirtualRows({ + records, + startingRealIndex: 0, + }); - store.set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount); + reapplyRowSelection(); + } - if (isDefined(records)) { - upsertRecordsInStore({ partialRecords: records }); + store.set(dataPagesLoadedCallbackState, []); - loadRecordsToVirtualRows({ - records, - startingRealIndex: 0, - }); + store.set(lastScrollPositionCallbackState, 0); + store.set(lastRealIndexSetCallbackState, null); + store.set(scrollAtRealIndexCallbackState, 0); - reapplyRowSelection(); - } + setIsRecordTableScrolledHorizontally(false); + setIsRecordTableScrolledVertically(false); + resetTableFocuses(); - store.set(dataPagesLoadedCallbackState, []); - - store.set(isInitializingVirtualTableDataLoadingCallbackState, false); - store.set(isRecordTableInitialLoading, false); - - store.set(lastScrollPositionCallbackState, 0); - store.set(lastRealIndexSetCallbackState, null); - store.set(scrollAtRealIndexCallbackState, 0); - - setIsRecordTableScrolledHorizontally(false); - setIsRecordTableScrolledVertically(false); - resetTableFocuses(); - - if (shouldScrollToStart) { - scrollTableToPosition({ - horizontalScrollInPx: 0, - verticalScrollInPx: 0, - }); + if (shouldScrollToStart) { + scrollTableToPosition({ + horizontalScrollInPx: 0, + verticalScrollInPx: 0, + }); + } + } finally { + store.set(isInitializingVirtualTableDataLoadingCallbackState, false); + store.set(isRecordTableInitialLoading, false); } }, [