Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { triggerUpdateRecordOptimisticEffectByBatch } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffectByBatch';
import { apiConfigState } from '@/client-config/states/apiConfigState';
import { useRemoveNavigationMenuItemByTargetRecordId } from '@/navigation-menu-item/hooks/useRemoveNavigationMenuItemByTargetRecordId';
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
Expand All @@ -16,11 +17,10 @@ import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useU
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { dispatchObjectRecordOperationBrowserEvent } from '@/object-record/utils/dispatchObjectRecordOperationBrowserEvent';
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
import { useRemoveNavigationMenuItemByTargetRecordId } from '@/navigation-menu-item/hooks/useRemoveNavigationMenuItemByTargetRecordId';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import { FeatureFlagKey } from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from '~/generated/graphql';
import { sleep } from '~/utils/sleep';

type useDeleteManyRecordProps = {
Expand Down Expand Up @@ -239,6 +239,7 @@ export const useDeleteManyRecords = ({
objectMetadataItem,
operation: {
type: 'delete-many',
deletedRecordIds: recordIdsToDelete,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export const useDeleteOneRecord = ({
objectMetadataItem,
operation: {
type: 'delete-one',
deletedRecordId: idToDelete,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { triggerUpdateRecordOptimisticEffectByBatch } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffectByBatch';
import { useRemoveNavigationMenuItemByTargetRecordId } from '@/navigation-menu-item/hooks/useRemoveNavigationMenuItemByTargetRecordId';
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
Expand All @@ -16,11 +17,10 @@ import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggr
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 { useRemoveNavigationMenuItemByTargetRecordId } from '@/navigation-menu-item/hooks/useRemoveNavigationMenuItemByTargetRecordId';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useCallback } from 'react';
import { FeatureFlagKey } from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from '~/generated/graphql';
import { sleep } from '~/utils/sleep';

const DEFAULT_DELAY_BETWEEN_MUTATIONS_MS = 50;
Expand Down Expand Up @@ -252,6 +252,7 @@ export const useIncrementalDeleteManyRecords = <T>({
objectMetadataItem,
operation: {
type: 'delete-many',
deletedRecordIds: allDeletedRecordIds,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export const useRestoreManyRecords = ({
objectMetadataItem,
operation: {
type: 'restore-many',
restoredRecords: restoredRecordsForThisBatch,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useListenToObjectRecordOperationBrowserEvent } from '@/object-record/hooks/useListenToObjectRecordOperationBrowserEvent';
import { useGetShouldInitializeRecordBoardForUpdateInputs } from '@/object-record/record-board/hooks/useGetShouldInitializeRecordBoardForUpdateInputs';
import { useRemoveRecordsFromBoard } from '@/object-record/record-board/hooks/useRemoveRecordsFromBoard';
import { useTriggerRecordBoardInitialQuery } from '@/object-record/record-board/hooks/useTriggerRecordBoardInitialQuery';
import { recordGroupFromGroupValueComponentFamilySelector } from '@/object-record/record-group/states/selectors/recordGroupFromGroupValueComponentFamilySelector';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
Expand Down Expand Up @@ -32,6 +33,8 @@ export const RecordBoardDataChangedEffect = () => {
recordIndexRecordIdsByGroupComponentFamilyState,
);

const { removeRecordsFromBoard } = useRemoveRecordsFromBoard();

const handleObjectRecordOperation = useRecoilCallback(
({ snapshot }) =>
(
Expand Down Expand Up @@ -126,6 +129,26 @@ export const RecordBoardDataChangedEffect = () => {
}
break;
}
case 'delete-one': {
const removedRecordId = objectRecordOperation.deletedRecordId;

removeRecordsFromBoard({
recordIdsToRemove: [removedRecordId],
});
return;
}
case 'delete-many': {
const removedRecordIds = objectRecordOperation.deletedRecordIds;

removeRecordsFromBoard({
recordIdsToRemove: removedRecordIds,
});
return;
}
case 'restore-many':
case 'restore-one': {
return;
}
default: {
triggerRecordBoardInitialQuery();
}
Expand All @@ -137,6 +160,7 @@ export const RecordBoardDataChangedEffect = () => {
recordIndexGroupFieldMetadataItemCallbackState,
recordGroupFromGroupValueCallbackState,
recordIndexRecordIdsByGroupCallbackState,
removeRecordsFromBoard,
],
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { recordGroupFromGroupValueComponentFamilySelector } from '@/object-record/record-group/states/selectors/recordGroupFromGroupValueComponentFamilySelector';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';

export const useRemoveRecordsFromBoard = () => {
const recordIndexGroupFieldMetadataItemCallbackState =
useRecoilComponentCallbackState(
recordIndexGroupFieldMetadataItemComponentState,
);

const recordGroupFromGroupValueFamilyCallbackState =
useRecoilComponentCallbackState(
recordGroupFromGroupValueComponentFamilySelector,
);

const recordIndexRecordIdsByGroupFamilyCallbackState =
useRecoilComponentCallbackState(
recordIndexRecordIdsByGroupComponentFamilyState,
);

const removeRecordsFromBoard = useRecoilCallback(
({ snapshot, set }) =>
({ recordIdsToRemove }: { recordIdsToRemove: string[] }) => {
const recordIdsToRemoveByGroup = new Map<string, string[]>();

for (const recordIdToRemove of recordIdsToRemove) {
const recordToRemove = getSnapshotValue(
snapshot,
recordStoreFamilyState(recordIdToRemove),
);

if (!isDefined(recordToRemove)) {
continue;
}

const recordIndexGroupFieldMetadataItem = getSnapshotValue(
snapshot,
recordIndexGroupFieldMetadataItemCallbackState,
);

if (!isDefined(recordIndexGroupFieldMetadataItem)) {
continue;
}

const recordGroupValue =
recordToRemove[recordIndexGroupFieldMetadataItem.name];

const recordGroupDefinitionFromGroupValue = getSnapshotValue(
snapshot,
recordGroupFromGroupValueFamilyCallbackState({ recordGroupValue }),
);

if (!isDefined(recordGroupDefinitionFromGroupValue)) {
continue;
}

const groupId = recordGroupDefinitionFromGroupValue.id;

if (!recordIdsToRemoveByGroup.has(groupId)) {
recordIdsToRemoveByGroup.set(groupId, []);
}

recordIdsToRemoveByGroup.get(groupId)?.push(recordIdToRemove);
}

for (const [
groupId,
recordIdsToRemoveInGroup,
] of recordIdsToRemoveByGroup) {
const currentRecordIdsForGroup = getSnapshotValue(
snapshot,
recordIndexRecordIdsByGroupFamilyCallbackState(groupId),
);

const recordIdsWithoutRemovedRecords =
currentRecordIdsForGroup.filter(
(recordId) => !recordIdsToRemoveInGroup.includes(recordId),
);

set(
recordIndexRecordIdsByGroupFamilyCallbackState(groupId),
recordIdsWithoutRemovedRecords,
);
}
},
[
recordIndexGroupFieldMetadataItemCallbackState,
recordGroupFromGroupValueFamilyCallbackState,
recordIndexRecordIdsByGroupFamilyCallbackState,
],
);

return {
removeRecordsFromBoard,
};
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useListenToObjectRecordOperationBrowserEvent } from '@/object-record/hooks/useListenToObjectRecordOperationBrowserEvent';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { SSE_TABLE_DEBOUNCE_TIME_IN_MS_TO_AVOID_SSE_OWN_EVENTS_RACE_CONDITION } from '@/object-record/record-table/virtualization/constants/SseTableDebounceTimeInMsToAvoidSseOwnEventsRaceCondition';
import { useGetShouldResetTableVirtualizationForUpdateInputs } from '@/object-record/record-table/virtualization/hooks/useGetShouldResetTableVirtualizationForUpdateInputs';
import { useResetVirtualizationBecauseDataChanged } from '@/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged';
import { type ObjectRecordOperationBrowserEventDetail } from '@/object-record/types/ObjectRecordOperationBrowserEventDetail';
import { useDebouncedCallback } from 'use-debounce';

export const RecordTableVirtualizedDataChangedEffect = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
Expand All @@ -15,6 +17,14 @@ export const RecordTableVirtualizedDataChangedEffect = () => {
const { getShouldResetTableVirtualizationForUpdateInputs } =
useGetShouldResetTableVirtualizationForUpdateInputs();

const debouncedResertVirtualizationBecauseDataChanged = useDebouncedCallback(
resetVirtualizationBecauseDataChanged,
SSE_TABLE_DEBOUNCE_TIME_IN_MS_TO_AVOID_SSE_OWN_EVENTS_RACE_CONDITION,
{
leading: false,
},
);

const handleObjectRecordOperation = (
objectRecordOperationEventDetail: ObjectRecordOperationBrowserEventDetail,
) => {
Expand All @@ -34,10 +44,10 @@ export const RecordTableVirtualizedDataChangedEffect = () => {
getShouldResetTableVirtualizationForUpdateInputs(updateInputs);

if (shouldResetForUpdateOperation) {
resetVirtualizationBecauseDataChanged();
debouncedResertVirtualizationBecauseDataChanged();
}
} else {
resetVirtualizationBecauseDataChanged();
debouncedResertVirtualizationBecauseDataChanged();
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SSE_TABLE_DEBOUNCE_TIME_IN_MS_TO_AVOID_SSE_OWN_EVENTS_RACE_CONDITION = 50;
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ export type ObjectRecordOperation =
createdRecord: ObjectRecord;
}
| {
type:
| 'create-many'
| 'destroy-one'
| 'destroy-many'
| 'delete-one'
| 'delete-many'
| 'restore-one'
| 'restore-many'
| 'merge-records';
type: 'delete-one';
deletedRecordId: string;
}
| {
type: 'delete-many';
deletedRecordIds: string[];
}
| {
type: 'restore-one';
restoredRecord: ObjectRecord;
}
| {
type: 'restore-many';
restoredRecords: ObjectRecord[];
}
| {
type: 'create-many' | 'merge-records' | 'destroy-one' | 'destroy-many';
};
Loading
Loading