Refactored object operation dispatch for SSE (#17174)
This PR refactors object record operation dispatch through a browser event instead of a state that was registering all operations. This is a cleaner pattern as it is a synchronous event code path instead of relying on a useEffect to watch state change, which is not ideal. We introduce this change first to then rely on this new pattern to dispatch SSE events in a following PR.
This commit is contained in:
+22
-17
@@ -1,12 +1,12 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useIncrementalFetchAndMutateRecords } from '@/object-record/hooks/useIncrementalFetchAndMutateRecords';
|
||||
import { useIncrementalUpdateManyRecords } from '@/object-record/hooks/useIncrementalUpdateManyRecords';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpdateManyRecords } from '@/object-record/hooks/useUpdateManyRecords';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useObjectMetadataItem');
|
||||
jest.mock('@/object-record/hooks/useRegisterObjectOperation');
|
||||
jest.mock('@/object-record/utils/dispatchObjectRecordOperationBrowserEvent');
|
||||
jest.mock('@/object-record/hooks/useUpdateManyRecords', () => ({
|
||||
useUpdateManyRecords: jest.fn(),
|
||||
}));
|
||||
@@ -18,7 +18,9 @@ jest.mock('@/object-record/hooks/useRefetchAggregateQueries', () => ({
|
||||
jest.mock('@/object-record/hooks/useIncrementalFetchAndMutateRecords');
|
||||
|
||||
const mockUseObjectMetadataItem = jest.mocked(useObjectMetadataItem);
|
||||
const mockUseRegisterObjectOperation = jest.mocked(useRegisterObjectOperation);
|
||||
const mockDispatchObjectRecordOperationBrowserEvent = jest.mocked(
|
||||
dispatchObjectRecordOperationBrowserEvent,
|
||||
);
|
||||
const mockUseUpdateManyRecords = jest.mocked(useUpdateManyRecords);
|
||||
const mockUseIncrementalFetchAndMutateRecords = jest.mocked(
|
||||
useIncrementalFetchAndMutateRecords,
|
||||
@@ -26,7 +28,6 @@ const mockUseIncrementalFetchAndMutateRecords = jest.mocked(
|
||||
|
||||
describe('useIncrementalUpdateManyRecords', () => {
|
||||
const mockUpdateManyRecords = jest.fn();
|
||||
const mockRegisterObjectOperation = jest.fn();
|
||||
const mockIncrementalFetchAndMutate = jest.fn();
|
||||
const mockUpdateProgress = jest.fn();
|
||||
|
||||
@@ -39,9 +40,7 @@ describe('useIncrementalUpdateManyRecords', () => {
|
||||
} as any,
|
||||
});
|
||||
|
||||
mockUseRegisterObjectOperation.mockReturnValue({
|
||||
registerObjectOperation: mockRegisterObjectOperation,
|
||||
});
|
||||
mockDispatchObjectRecordOperationBrowserEvent.mockImplementation(jest.fn());
|
||||
|
||||
mockUseUpdateManyRecords.mockReturnValue({
|
||||
updateManyRecords: mockUpdateManyRecords,
|
||||
@@ -85,18 +84,24 @@ describe('useIncrementalUpdateManyRecords', () => {
|
||||
abortSignal: expect.any(AbortSignal),
|
||||
});
|
||||
expect(mockUpdateProgress).toHaveBeenCalledWith(2, 2);
|
||||
expect(mockRegisterObjectOperation).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
expect(mockDispatchObjectRecordOperationBrowserEvent).toHaveBeenCalledWith({
|
||||
objectMetadataItem: expect.anything(),
|
||||
operation: {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: [
|
||||
{ id: '1', name: 'New Name' },
|
||||
{ id: '2', name: 'New Name' },
|
||||
{
|
||||
recordId: '1',
|
||||
updatedFields: [{ name: 'New Name' }],
|
||||
},
|
||||
{
|
||||
recordId: '2',
|
||||
updatedFields: [{ name: 'New Name' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass abortSignal to updateManyRecords', async () => {
|
||||
@@ -140,14 +145,14 @@ describe('useIncrementalUpdateManyRecords', () => {
|
||||
result.current.incrementalUpdateManyRecords({ name: 'New Name' }),
|
||||
).rejects.toThrow('Process failed');
|
||||
|
||||
expect(mockRegisterObjectOperation).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
expect(mockDispatchObjectRecordOperationBrowserEvent).toHaveBeenCalledWith({
|
||||
objectMetadataItem: expect.anything(),
|
||||
operation: {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: [],
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
type useCreateManyRecordsProps,
|
||||
} from '@/object-record/hooks/useCreateManyRecords';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { t } from '@lingui/core/macro';
|
||||
@@ -27,8 +27,6 @@ export const useBatchCreateManyRecords = <
|
||||
setBatchedRecordsCount?: (count: number) => void;
|
||||
abortController?: AbortController;
|
||||
}) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const { createManyRecords } = useCreateManyRecords({
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
@@ -100,7 +98,10 @@ export const useBatchCreateManyRecords = <
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, { type: 'create-many' });
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-many' },
|
||||
});
|
||||
|
||||
return allCreatedRecords;
|
||||
};
|
||||
|
||||
@@ -12,18 +12,18 @@ import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordF
|
||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type FieldActorForInputValue } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type PartialObjectRecordWithId = Partial<ObjectRecord> & {
|
||||
@@ -51,7 +51,6 @@ export const useCreateManyRecords = <
|
||||
shouldMatchRootQueryFilter,
|
||||
shouldRefetchAggregateQueries = true,
|
||||
}: useCreateManyRecordsProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
@@ -243,7 +242,10 @@ export const useCreateManyRecords = <
|
||||
await refetchAggregateQueries();
|
||||
}
|
||||
|
||||
registerObjectOperation(objectMetadataItem, { type: 'create-many' });
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-many' },
|
||||
});
|
||||
|
||||
return createdObjects.data?.[mutationResponseField] ?? [];
|
||||
};
|
||||
|
||||
@@ -13,18 +13,18 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename
|
||||
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticCreateRecordBaseRecordInput } from '@/object-record/utils/computeOptimisticCreateRecordBaseRecordInput';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { CustomError, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type useCreateOneRecordProps = {
|
||||
@@ -43,7 +43,6 @@ export const useCreateOneRecord = <
|
||||
shouldMatchRootQueryFilter,
|
||||
}: useCreateOneRecordProps) => {
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -187,7 +186,10 @@ export const useCreateOneRecord = <
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, { type: 'create-one' });
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: { type: 'create-one' },
|
||||
});
|
||||
|
||||
if (!isDefined(createdObject.data?.[mutationResponseField])) {
|
||||
throw new CustomError('Failed to create record');
|
||||
|
||||
@@ -12,9 +12,9 @@ import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'
|
||||
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -34,7 +34,6 @@ export type DeleteManyRecordsProps = {
|
||||
export const useDeleteManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
@@ -224,8 +223,11 @@ export const useDeleteManyRecords = ({
|
||||
}
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'delete-many',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'delete-many',
|
||||
},
|
||||
});
|
||||
|
||||
return deletedRecords;
|
||||
|
||||
@@ -12,9 +12,9 @@ import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordF
|
||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -26,7 +26,6 @@ type useDeleteOneRecordProps = {
|
||||
export const useDeleteOneRecord = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
@@ -165,8 +164,11 @@ export const useDeleteOneRecord = ({
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'delete-one',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'delete-one',
|
||||
},
|
||||
});
|
||||
|
||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||
@@ -178,7 +180,6 @@ export const useDeleteOneRecord = ({
|
||||
objectMetadataItems,
|
||||
deleteOneRecordMutation,
|
||||
refetchAggregateQueries,
|
||||
registerObjectOperation,
|
||||
mutationResponseField,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
upsertRecordsInStore,
|
||||
|
||||
@@ -9,9 +9,9 @@ import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMu
|
||||
import { useDestroyManyRecordsMutation } from '@/object-record/hooks/useDestroyManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
@@ -31,7 +31,6 @@ export type DestroyManyRecordsProps = {
|
||||
export const useDestroyManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useDestroyManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
@@ -145,8 +144,11 @@ export const useDestroyManyRecords = ({
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'destroy-many',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'destroy-many',
|
||||
},
|
||||
});
|
||||
|
||||
return destroyedRecords;
|
||||
|
||||
@@ -8,8 +8,8 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getDestroyOneRecordMutationResponseField } from '@/object-record/utils/getDestroyOneRecordMutationResponseField';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -21,7 +21,6 @@ type useDestroyOneRecordProps = {
|
||||
export const useDestroyOneRecord = ({
|
||||
objectNameSingular,
|
||||
}: useDestroyOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
@@ -89,8 +88,11 @@ export const useDestroyOneRecord = ({
|
||||
throw error;
|
||||
});
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'destroy-one',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'destroy-one',
|
||||
},
|
||||
});
|
||||
|
||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||
@@ -101,7 +103,6 @@ export const useDestroyOneRecord = ({
|
||||
destroyOneRecordMutation,
|
||||
mutationResponseField,
|
||||
objectNameSingular,
|
||||
registerObjectOperation,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
upsertRecordsInStore,
|
||||
|
||||
+6
-4
@@ -13,9 +13,9 @@ import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMor
|
||||
import { useIncrementalFetchAndMutateRecords } from '@/object-record/hooks/useIncrementalFetchAndMutateRecords';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
@@ -40,7 +40,6 @@ export const useIncrementalDeleteManyRecords = <T>({
|
||||
delayInMsBetweenMutations = DEFAULT_DELAY_BETWEEN_MUTATIONS_MS,
|
||||
skipOptimisticEffect = false,
|
||||
}: UseIncrementalDeleteManyRecordsParams<T>) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const mutationPageSize = pageSize;
|
||||
@@ -235,8 +234,11 @@ export const useIncrementalDeleteManyRecords = <T>({
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'delete-many',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'delete-many',
|
||||
},
|
||||
});
|
||||
|
||||
return totalDeletedCount;
|
||||
|
||||
+6
-4
@@ -10,9 +10,9 @@ import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMor
|
||||
import { useIncrementalFetchAndMutateRecords } from '@/object-record/hooks/useIncrementalFetchAndMutateRecords';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
@@ -37,7 +37,6 @@ export const useIncrementalDestroyManyRecords = <T>({
|
||||
delayInMsBetweenMutations = DEFAULT_DELAY_BETWEEN_MUTATIONS_MS,
|
||||
skipOptimisticEffect = false,
|
||||
}: UseIncrementalDestroyManyRecordsParams<T>) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const mutationPageSize = pageSize;
|
||||
@@ -165,8 +164,11 @@ export const useIncrementalDestroyManyRecords = <T>({
|
||||
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'destroy-many',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'destroy-many',
|
||||
},
|
||||
});
|
||||
|
||||
return totalDestroyedCount;
|
||||
|
||||
+12
-10
@@ -3,9 +3,10 @@ import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryP
|
||||
import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMoreRecordsWithPagination';
|
||||
import { useIncrementalFetchAndMutateRecords } from '@/object-record/hooks/useIncrementalFetchAndMutateRecords';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpdateManyRecords } from '@/object-record/hooks/useUpdateManyRecords';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getUpdatedFieldsFromRecordInput } from '@/object-record/utils/getUpdatedFieldsFromRecordInput';
|
||||
|
||||
const DEFAULT_DELAY_BETWEEN_MUTATIONS_MS = 50;
|
||||
|
||||
@@ -28,8 +29,6 @@ export const useIncrementalUpdateManyRecords = <
|
||||
pageSize = DEFAULT_QUERY_PAGE_SIZE,
|
||||
delayInMsBetweenMutations = DEFAULT_DELAY_BETWEEN_MUTATIONS_MS,
|
||||
}: UseIncrementalUpdateManyRecordsParams<T>) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
@@ -83,13 +82,16 @@ export const useIncrementalUpdateManyRecords = <
|
||||
} finally {
|
||||
await refetchAggregateQueries();
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: allUpdatedRecordIds.map((id) => ({
|
||||
id,
|
||||
...fieldsToUpdate,
|
||||
})),
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: allUpdatedRecordIds.map((recordId) => ({
|
||||
recordId,
|
||||
updatedFields: getUpdatedFieldsFromRecordInput(fieldsToUpdate),
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { OBJECT_RECORD_OPERATION_BROWSER_EVENT_NAME } from '@/object-record/constants/ObjectRecordOperationBrowserEventName';
|
||||
import { type ObjectRecordOperation } from '@/object-record/types/ObjectRecordOperation';
|
||||
import { type ObjectRecordOperationBrowserEventDetail } from '@/object-record/types/ObjectRecordOperationBrowserEventDetail';
|
||||
import { useEffect } from 'react';
|
||||
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
|
||||
|
||||
export const useListenToObjectRecordOperationBrowserEvent = ({
|
||||
onObjectRecordOperationBrowserEvent,
|
||||
objectMetadataItemId,
|
||||
operationTypes,
|
||||
}: {
|
||||
onObjectRecordOperationBrowserEvent: (
|
||||
detail: ObjectRecordOperationBrowserEventDetail,
|
||||
) => void;
|
||||
objectMetadataItemId?: string;
|
||||
operationTypes?: ObjectRecordOperation['type'][];
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
const handleObjectRecordOperationEvent = (event: Event) => {
|
||||
const detail = (
|
||||
event as CustomEvent<ObjectRecordOperationBrowserEventDetail>
|
||||
).detail;
|
||||
|
||||
if (
|
||||
isDefined(objectMetadataItemId) &&
|
||||
detail.objectMetadataItem.id !== objectMetadataItemId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isNonEmptyArray(operationTypes) &&
|
||||
!operationTypes.includes(detail.operation.type)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
onObjectRecordOperationBrowserEvent(detail);
|
||||
};
|
||||
|
||||
window.addEventListener(
|
||||
OBJECT_RECORD_OPERATION_BROWSER_EVENT_NAME,
|
||||
handleObjectRecordOperationEvent,
|
||||
);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener(
|
||||
OBJECT_RECORD_OPERATION_BROWSER_EVENT_NAME,
|
||||
handleObjectRecordOperationEvent,
|
||||
);
|
||||
};
|
||||
}, [
|
||||
objectMetadataItemId,
|
||||
onObjectRecordOperationBrowserEvent,
|
||||
operationTypes,
|
||||
]);
|
||||
};
|
||||
@@ -3,15 +3,15 @@ import { useCallback, useState } from 'react';
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { useMergeManyRecordsMutation } from '@/object-record/hooks/useMergeManyRecordsMutation';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getMergeManyRecordsMutationResponseField } from '@/object-record/utils/getMergeManyRecordsMutationResponseField';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types';
|
||||
|
||||
export type MergeManySettings = {
|
||||
conflictPriorityIndex: number;
|
||||
@@ -28,7 +28,6 @@ export const useMergeManyRecords = <
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
}: UseMergeManyRecordsProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -103,8 +102,11 @@ export const useMergeManyRecords = <
|
||||
|
||||
if (!preview) {
|
||||
await refetchAggregateQueries();
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'merge-records',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'merge-records',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,7 +123,6 @@ export const useMergeManyRecords = <
|
||||
findOneRecordQuery,
|
||||
findDuplicateRecordsQuery,
|
||||
refetchAggregateQueries,
|
||||
registerObjectOperation,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import {
|
||||
objectOperationsState,
|
||||
type ObjectOperationData,
|
||||
} from '@/object-record/states/objectOperationsComponentState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const useRegisterObjectOperation = () => {
|
||||
const registerObjectOperation = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(objectMetadataItem: ObjectMetadataItem, data: ObjectOperationData) => {
|
||||
set(objectOperationsState, (currentValue) => {
|
||||
const newValue = currentValue.concat();
|
||||
|
||||
newValue.push({
|
||||
id: v4(),
|
||||
timestamp: +new Date(),
|
||||
objectMetadataItemId: objectMetadataItem.id,
|
||||
data,
|
||||
});
|
||||
|
||||
return newValue;
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
registerObjectOperation,
|
||||
};
|
||||
};
|
||||
@@ -9,10 +9,10 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useRestoreManyRecordsMutation } from '@/object-record/hooks/useRestoreManyRecordsMutation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getRestoreManyRecordsMutationResponseField } from '@/object-record/utils/getRestoreManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
@@ -32,7 +32,6 @@ type RestoreManyRecordsProps = {
|
||||
export const useRestoreManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useRestoreManyRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
@@ -200,8 +199,11 @@ export const useRestoreManyRecords = ({
|
||||
|
||||
restoredRecords.push(...restoredRecordsForThisBatch);
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'restore-many',
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'restore-many',
|
||||
},
|
||||
});
|
||||
|
||||
if (isDefined(delayInMsBetweenRequests)) {
|
||||
|
||||
@@ -13,10 +13,11 @@ import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpdateManyRecordsMutation } from '@/object-record/hooks/useUpdateManyRecordsMutation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getUpdatedFieldsFromRecordInput } from '@/object-record/utils/getUpdatedFieldsFromRecordInput';
|
||||
import { getUpdateManyRecordsMutationResponseField } from '@/object-record/utils/getUpdateManyRecordsMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -42,7 +43,6 @@ export const useUpdateManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
}: UseUpdateManyRecordsProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const apiConfig = useRecoilValue(apiConfigState);
|
||||
|
||||
@@ -265,13 +265,17 @@ export const useUpdateManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
}
|
||||
|
||||
if (!skipRegisterObjectOperation) {
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: recordIdsToUpdate.map((id) => ({
|
||||
id,
|
||||
...updateOneRecordInput,
|
||||
})),
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'update-many',
|
||||
result: {
|
||||
updateInputs: recordIdsToUpdate.map((recordId) => ({
|
||||
recordId,
|
||||
updatedFields:
|
||||
getUpdatedFieldsFromRecordInput(updateOneRecordInput),
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graph
|
||||
import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getUpdatedFieldsFromRecordInput } from '@/object-record/utils/getUpdatedFieldsFromRecordInput';
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
@@ -39,7 +40,6 @@ export const useUpdateOneRecord = <
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
}: useUpdateOneRecordProps) => {
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
@@ -236,9 +236,18 @@ export const useUpdateOneRecord = <
|
||||
|
||||
const udpatedRecord = updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'update-one',
|
||||
result: { updateInput: updateOneRecordInput },
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'update-one',
|
||||
result: {
|
||||
updateInput: {
|
||||
recordId: idToUpdate,
|
||||
updatedFields:
|
||||
getUpdatedFieldsFromRecordInput(updateOneRecordInput),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return udpatedRecord;
|
||||
|
||||
@@ -12,10 +12,12 @@ import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/
|
||||
import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject';
|
||||
import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRegisterObjectOperation } from '@/object-record/hooks/useRegisterObjectOperation';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
|
||||
import { getUpdatedFieldsFromRecordInput } from '@/object-record/utils/getUpdatedFieldsFromRecordInput';
|
||||
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
@@ -33,7 +35,6 @@ type UpdateOneRecordArgs<UpdatedObjectRecord> = {
|
||||
export const useUpdateOneRecordV2 = () => {
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const { registerObjectOperation } = useRegisterObjectOperation();
|
||||
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
@@ -238,9 +239,18 @@ export const useUpdateOneRecordV2 = () => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
registerObjectOperation(objectMetadataItem, {
|
||||
type: 'update-one',
|
||||
result: { updateInput: updateOneRecordInput },
|
||||
dispatchObjectRecordOperationBrowserEvent({
|
||||
objectMetadataItem,
|
||||
operation: {
|
||||
type: 'update-one',
|
||||
result: {
|
||||
updateInput: {
|
||||
recordId: idToUpdate,
|
||||
updatedFields:
|
||||
getUpdatedFieldsFromRecordInput(updateOneRecordInput),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||
|
||||
Reference in New Issue
Block a user