Improved table flash on reload (#16419)

This PR fixes https://github.com/twentyhq/core-team-issues/issues/1732

There is still some room for improvement but the main goal is reached :
removing the flash effect each time the table virtualization has to
recompute due to an update after initial loading.

# QA

## Create 


https://github.com/user-attachments/assets/1b4fc307-42ce-4ba6-b557-68ac7fcad40f

## Update with sort


https://github.com/user-attachments/assets/e0700f44-8926-4395-8ab8-b32773f21de8

## Update with filter


https://github.com/user-attachments/assets/d325f85a-1a7b-4366-aac3-250331be7575

## Soft delete


https://github.com/user-attachments/assets/2c980183-c637-4aa7-a0ca-244e61396b20

## Restore and destroy


https://github.com/user-attachments/assets/01af9ec7-b442-4686-a1d7-ea1fc543fe62

## Drag & drop


https://github.com/user-attachments/assets/bba76bdb-d4ec-433d-b44e-37fdb9952b06

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2025-12-11 04:14:20 -10:00
committed by GitHub
parent 3cea19baf4
commit ec8773437e
30 changed files with 446 additions and 156 deletions
@@ -100,7 +100,7 @@ export const useBatchCreateManyRecords = <
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, { type: 'create-many' });
registerObjectOperation(objectMetadataItem, { type: 'create-many' });
return allCreatedRecords;
};
@@ -243,7 +243,7 @@ export const useCreateManyRecords = <
await refetchAggregateQueries();
}
registerObjectOperation(objectNameSingular, { type: 'create-many' });
registerObjectOperation(objectMetadataItem, { type: 'create-many' });
return createdObjects.data?.[mutationResponseField] ?? [];
};
@@ -187,7 +187,7 @@ export const useCreateOneRecord = <
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, { type: 'create-one' });
registerObjectOperation(objectMetadataItem, { type: 'create-one' });
if (!isDefined(createdObject.data?.[mutationResponseField])) {
throw new CustomError('Failed to create record');
@@ -224,7 +224,7 @@ export const useDeleteManyRecords = ({
}
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'delete-many',
});
@@ -165,7 +165,7 @@ export const useDeleteOneRecord = ({
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'delete-one',
});
@@ -179,7 +179,6 @@ export const useDeleteOneRecord = ({
deleteOneRecordMutation,
refetchAggregateQueries,
registerObjectOperation,
objectNameSingular,
mutationResponseField,
objectPermissionsByObjectMetadataId,
upsertRecordsInStore,
@@ -145,7 +145,7 @@ export const useDestroyManyRecords = ({
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'destroy-many',
});
@@ -89,7 +89,7 @@ export const useDestroyOneRecord = ({
throw error;
});
registerObjectOperation(objectMetadataItem.nameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'destroy-one',
});
@@ -225,7 +225,7 @@ export const useIncrementalDeleteManyRecords = <T>({
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'delete-many',
});
@@ -155,7 +155,7 @@ export const useIncrementalDestroyManyRecords = <T>({
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'destroy-many',
});
@@ -103,7 +103,7 @@ export const useMergeManyRecords = <
if (!preview) {
await refetchAggregateQueries();
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'merge-records',
});
}
@@ -115,14 +115,13 @@ export const useMergeManyRecords = <
}
},
[
objectMetadataItem.namePlural,
objectMetadataItem,
apolloCoreClient,
mergeManyRecordsMutation,
findOneRecordQuery,
findDuplicateRecordsQuery,
registerObjectOperation,
objectNameSingular,
refetchAggregateQueries,
registerObjectOperation,
],
);
@@ -1,30 +1,27 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import {
objectOperationsState,
type ObjectOperationData,
objectOperationsByObjectNameSingularFamilyState,
} from '@/object-record/states/objectOperationsByObjectNameSingularFamilyState';
} from '@/object-record/states/objectOperationsComponentState';
import { useRecoilCallback } from 'recoil';
import { v4 } from 'uuid';
export const useRegisterObjectOperation = () => {
const registerObjectOperation = useRecoilCallback(
({ set }) =>
(objectNameSingular: string, data: ObjectOperationData) => {
set(
objectOperationsByObjectNameSingularFamilyState({
objectNameSingular,
}),
(currentValue) => {
const newValue = currentValue.concat();
(objectMetadataItem: ObjectMetadataItem, data: ObjectOperationData) => {
set(objectOperationsState, (currentValue) => {
const newValue = currentValue.concat();
newValue.push({
id: v4(),
timestamp: +new Date(),
data,
});
newValue.push({
id: v4(),
timestamp: +new Date(),
objectMetadataItemId: objectMetadataItem.id,
data,
});
return newValue;
},
);
return newValue;
});
},
[],
);
@@ -200,7 +200,7 @@ export const useRestoreManyRecords = ({
restoredRecords.push(...restoredRecordsForThisBatch);
registerObjectOperation(objectMetadataItem.nameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'restore-many',
});
@@ -230,9 +230,9 @@ export const useUpdateOneRecord = <
const udpatedRecord = updatedRecord?.data?.[mutationResponseField] ?? null;
registerObjectOperation(objectNameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'update-one',
result: { updatedRecord, updateInput: updateOneRecordInput },
result: { updateInput: updateOneRecordInput },
});
return udpatedRecord;
@@ -238,10 +238,11 @@ export const useUpdateOneRecordV2 = () => {
throw error;
});
registerObjectOperation(objectMetadataItem.nameSingular, {
registerObjectOperation(objectMetadataItem, {
type: 'update-one',
result: { updatedRecord, updateInput: updateOneRecordInput },
result: { updateInput: updateOneRecordInput },
});
return updatedRecord?.data?.[mutationResponseField] ?? null;
};