diff --git a/packages/twenty-front/src/modules/object-record/constants/DefaultMutationBatchSize.ts b/packages/twenty-front/src/modules/object-record/constants/DefaultMutationBatchSize.ts new file mode 100644 index 0000000000..6864c5e05e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/constants/DefaultMutationBatchSize.ts @@ -0,0 +1 @@ +export const DEFAULT_MUTATION_BATCH_SIZE = 30; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useDeleteManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useDeleteManyRecords.ts index f189d516da..e21aa9b6f1 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useDeleteManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useDeleteManyRecords.ts @@ -1,9 +1,12 @@ import { useApolloClient } from '@apollo/client'; +import { useRecoilValue } from 'recoil'; import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect'; +import { apiConfigState } from '@/client-config/states/apiConfigState'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache'; +import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize'; import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation'; import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField'; import { isDefined } from '~/utils/isDefined'; @@ -41,44 +44,69 @@ export const useDeleteManyRecords = ({ objectMetadataItem.namePlural, ); + const apiConfig = useRecoilValue(apiConfigState); + const maxRecords = apiConfig?.mutationMaximumAffectedRecords; + const deleteManyRecords = async ( idsToDelete: string[], options?: DeleteManyRecordsOptions, ) => { - const deletedRecords = await apolloClient.mutate({ - mutation: deleteManyRecordsMutation, - variables: { - filter: { id: { in: idsToDelete } }, - }, - optimisticResponse: options?.skipOptimisticEffect - ? undefined - : { - [mutationResponseField]: idsToDelete.map((idToDelete) => ({ - __typename: capitalize(objectNameSingular), - id: idToDelete, - })), - }, - update: options?.skipOptimisticEffect - ? undefined - : (cache, { data }) => { - const records = data?.[mutationResponseField]; + const numberOfBatches = Math.ceil( + idsToDelete.length / DEFAULT_MUTATION_BATCH_SIZE, + ); - if (!records?.length) return; + const deletedRecords = []; - const cachedRecords = records - .map((record) => getRecordFromCache(record.id, cache)) - .filter(isDefined); + for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) { + const batchIds = idsToDelete.slice( + batchIndex * DEFAULT_MUTATION_BATCH_SIZE, + (batchIndex + 1) * DEFAULT_MUTATION_BATCH_SIZE, + ); - triggerDeleteRecordsOptimisticEffect({ - cache, - objectMetadataItem, - recordsToDelete: cachedRecords, - objectMetadataItems, - }); - }, - }); + console.log({ + batchIds, + }); - return deletedRecords.data?.[mutationResponseField] ?? null; + const deletedRecordsResponse = await apolloClient.mutate({ + mutation: deleteManyRecordsMutation, + variables: { + filter: { id: { in: batchIds } }, + }, + optimisticResponse: options?.skipOptimisticEffect + ? undefined + : { + [mutationResponseField]: batchIds.map((idToDelete) => ({ + __typename: capitalize(objectNameSingular), + id: idToDelete, + })), + }, + update: options?.skipOptimisticEffect + ? undefined + : (cache, { data }) => { + const records = data?.[mutationResponseField]; + + if (!records?.length) return; + + const cachedRecords = records + .map((record) => getRecordFromCache(record.id, cache)) + .filter(isDefined); + + triggerDeleteRecordsOptimisticEffect({ + cache, + objectMetadataItem, + recordsToDelete: cachedRecords, + objectMetadataItems, + }); + }, + }); + + const deletedRecordsForThisBatch = + deletedRecordsResponse.data?.[mutationResponseField] ?? []; + + deletedRecords.push(...deletedRecordsForThisBatch); + } + + return deletedRecords; }; return { deleteManyRecords }; diff --git a/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx b/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx index 70e35b0a59..0a43749ea8 100644 --- a/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx +++ b/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx @@ -1,6 +1,11 @@ import { useCallback, useMemo, useState } from 'react'; import { isNonEmptyString } from '@sniptt/guards'; -import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil'; +import { + useRecoilCallback, + useRecoilValue, + useRecoilValue, + useSetRecoilState, +} from 'recoil'; import { IconClick, IconFileExport,