Fix optimistic effect deletedAt (#7606)
In this PR, I'm fixing part of the impact of soft deletion on optimistic
rendering.
## Backend Vision
1) Backend endpoints will not return soft deleted records (having
deletedAt set) by default. To get the softDeleted records, we will pass
a { withSoftDelete: true } additional param in the query.
2) Record relations will NEVER contain softDeleted relations
## Backend current state
Right now, we have the following behavior:
- if the query filters do not mention deletedAt, we don't return
softDeletedRecords
- if the query filters mention deletedAt, we take it into consideration.
Meaning that if we want to have the softDeleted records in any way we
need to do { or: [ deletedAt: NULL, deletedAt: NOT_NULL] }
## Optimistic rendering strategy
1) useDestroyOne/Many is triggering destroyOptimisticEffects (previously
deleteOptimisticEffects)
2) UseDeleteOne/Many and useRestoreOne/Many are actually triggering
updateOptimisticEffects (as they only update deletedAt field) AND we
need updateOptimisticEffects to take into account deletedAt (future
withSoftDelete: true) filter.
This commit is contained in:
@ -7,15 +7,15 @@ import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/is
|
||||
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const triggerDeleteRecordsOptimisticEffect = ({
|
||||
export const triggerDestroyRecordsOptimisticEffect = ({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDelete,
|
||||
recordsToDestroy,
|
||||
objectMetadataItems,
|
||||
}: {
|
||||
cache: ApolloCache<unknown>;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
recordsToDelete: RecordGqlNode[];
|
||||
recordsToDestroy: RecordGqlNode[];
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
}) => {
|
||||
cache.modify<StoreObject>({
|
||||
@ -36,7 +36,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
||||
|
||||
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
||||
|
||||
const recordIdsToDelete = recordsToDelete.map(({ id }) => id);
|
||||
const recordIdsToDelete = recordsToDestroy.map(({ id }) => id);
|
||||
|
||||
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
||||
'edges',
|
||||
@ -69,20 +69,15 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
||||
},
|
||||
});
|
||||
|
||||
recordsToDelete.forEach((recordToDelete) => {
|
||||
recordsToDestroy.forEach((recordToDestroy) => {
|
||||
triggerUpdateRelationsOptimisticEffect({
|
||||
cache,
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
currentSourceRecord: recordToDelete,
|
||||
currentSourceRecord: recordToDestroy,
|
||||
updatedSourceRecord: null,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
cache.modify({
|
||||
id: cache.identify(recordToDelete),
|
||||
fields: {
|
||||
deletedAt: () => recordToDelete.deletedAt,
|
||||
},
|
||||
});
|
||||
cache.evict({ id: cache.identify(recordToDestroy) });
|
||||
});
|
||||
};
|
||||
@ -65,48 +65,43 @@ export const triggerUpdateRecordOptimisticEffect = ({
|
||||
const rootQueryFilter = rootQueryVariables?.filter;
|
||||
const rootQueryOrderBy = rootQueryVariables?.orderBy;
|
||||
|
||||
const shouldTryToMatchFilter = isDefined(rootQueryFilter);
|
||||
const updatedRecordMatchesThisRootQueryFilter = isRecordMatchingFilter({
|
||||
record: updatedRecord,
|
||||
filter: rootQueryFilter ?? {},
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
if (shouldTryToMatchFilter) {
|
||||
const updatedRecordMatchesThisRootQueryFilter =
|
||||
isRecordMatchingFilter({
|
||||
record: updatedRecord,
|
||||
filter: rootQueryFilter,
|
||||
objectMetadataItem,
|
||||
const updatedRecordIndexInRootQueryEdges =
|
||||
rootQueryCurrentEdges.findIndex(
|
||||
(cachedEdge) =>
|
||||
readField('id', cachedEdge.node) === updatedRecord.id,
|
||||
);
|
||||
|
||||
const updatedRecordFoundInRootQueryEdges =
|
||||
updatedRecordIndexInRootQueryEdges > -1;
|
||||
|
||||
const updatedRecordShouldBeAddedToRootQueryEdges =
|
||||
updatedRecordMatchesThisRootQueryFilter &&
|
||||
!updatedRecordFoundInRootQueryEdges;
|
||||
|
||||
const updatedRecordShouldBeRemovedFromRootQueryEdges =
|
||||
!updatedRecordMatchesThisRootQueryFilter &&
|
||||
updatedRecordFoundInRootQueryEdges;
|
||||
|
||||
if (updatedRecordShouldBeAddedToRootQueryEdges) {
|
||||
const updatedRecordNodeReference = toReference(updatedRecord);
|
||||
|
||||
if (isDefined(updatedRecordNodeReference)) {
|
||||
rootQueryNextEdges.push({
|
||||
__typename: getEdgeTypename(objectMetadataItem.nameSingular),
|
||||
node: updatedRecordNodeReference,
|
||||
cursor: '',
|
||||
});
|
||||
|
||||
const updatedRecordIndexInRootQueryEdges =
|
||||
rootQueryCurrentEdges.findIndex(
|
||||
(cachedEdge) =>
|
||||
readField('id', cachedEdge.node) === updatedRecord.id,
|
||||
);
|
||||
|
||||
const updatedRecordFoundInRootQueryEdges =
|
||||
updatedRecordIndexInRootQueryEdges > -1;
|
||||
|
||||
const updatedRecordShouldBeAddedToRootQueryEdges =
|
||||
updatedRecordMatchesThisRootQueryFilter &&
|
||||
!updatedRecordFoundInRootQueryEdges;
|
||||
|
||||
const updatedRecordShouldBeRemovedFromRootQueryEdges =
|
||||
!updatedRecordMatchesThisRootQueryFilter &&
|
||||
updatedRecordFoundInRootQueryEdges;
|
||||
|
||||
if (updatedRecordShouldBeAddedToRootQueryEdges) {
|
||||
const updatedRecordNodeReference = toReference(updatedRecord);
|
||||
|
||||
if (isDefined(updatedRecordNodeReference)) {
|
||||
rootQueryNextEdges.push({
|
||||
__typename: getEdgeTypename(objectMetadataItem.nameSingular),
|
||||
node: updatedRecordNodeReference,
|
||||
cursor: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedRecordShouldBeRemovedFromRootQueryEdges) {
|
||||
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
|
||||
}
|
||||
if (updatedRecordShouldBeRemovedFromRootQueryEdges) {
|
||||
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
|
||||
}
|
||||
|
||||
const rootQueryNextEdgesShouldBeSorted = isDefined(rootQueryOrderBy);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ApolloCache } from '@apollo/client';
|
||||
|
||||
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
|
||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -122,10 +122,10 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
||||
);
|
||||
|
||||
if (shouldCascadeDeleteTargetRecords) {
|
||||
triggerDeleteRecordsOptimisticEffect({
|
||||
triggerDestroyRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem: fullTargetObjectMetadataItem,
|
||||
recordsToDelete: targetRecordsToDetachFrom,
|
||||
recordsToDestroy: targetRecordsToDetachFrom,
|
||||
objectMetadataItems,
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user