mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-24 12:34:10 +03:00
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:
parent
d350143c92
commit
7b96be6f8c
@ -7,15 +7,15 @@ import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/is
|
|||||||
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const triggerDeleteRecordsOptimisticEffect = ({
|
export const triggerDestroyRecordsOptimisticEffect = ({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete,
|
recordsToDestroy,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
}: {
|
}: {
|
||||||
cache: ApolloCache<unknown>;
|
cache: ApolloCache<unknown>;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
recordsToDelete: RecordGqlNode[];
|
recordsToDestroy: RecordGqlNode[];
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
}) => {
|
}) => {
|
||||||
cache.modify<StoreObject>({
|
cache.modify<StoreObject>({
|
||||||
@ -36,7 +36,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
|||||||
|
|
||||||
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
||||||
|
|
||||||
const recordIdsToDelete = recordsToDelete.map(({ id }) => id);
|
const recordIdsToDelete = recordsToDestroy.map(({ id }) => id);
|
||||||
|
|
||||||
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
||||||
'edges',
|
'edges',
|
||||||
@ -69,20 +69,15 @@ export const triggerDeleteRecordsOptimisticEffect = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
recordsToDelete.forEach((recordToDelete) => {
|
recordsToDestroy.forEach((recordToDestroy) => {
|
||||||
triggerUpdateRelationsOptimisticEffect({
|
triggerUpdateRelationsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
sourceObjectMetadataItem: objectMetadataItem,
|
sourceObjectMetadataItem: objectMetadataItem,
|
||||||
currentSourceRecord: recordToDelete,
|
currentSourceRecord: recordToDestroy,
|
||||||
updatedSourceRecord: null,
|
updatedSourceRecord: null,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
cache.modify({
|
cache.evict({ id: cache.identify(recordToDestroy) });
|
||||||
id: cache.identify(recordToDelete),
|
|
||||||
fields: {
|
|
||||||
deletedAt: () => recordToDelete.deletedAt,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
@ -65,48 +65,43 @@ export const triggerUpdateRecordOptimisticEffect = ({
|
|||||||
const rootQueryFilter = rootQueryVariables?.filter;
|
const rootQueryFilter = rootQueryVariables?.filter;
|
||||||
const rootQueryOrderBy = rootQueryVariables?.orderBy;
|
const rootQueryOrderBy = rootQueryVariables?.orderBy;
|
||||||
|
|
||||||
const shouldTryToMatchFilter = isDefined(rootQueryFilter);
|
const updatedRecordMatchesThisRootQueryFilter = isRecordMatchingFilter({
|
||||||
|
record: updatedRecord,
|
||||||
|
filter: rootQueryFilter ?? {},
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
if (shouldTryToMatchFilter) {
|
const updatedRecordIndexInRootQueryEdges =
|
||||||
const updatedRecordMatchesThisRootQueryFilter =
|
rootQueryCurrentEdges.findIndex(
|
||||||
isRecordMatchingFilter({
|
(cachedEdge) =>
|
||||||
record: updatedRecord,
|
readField('id', cachedEdge.node) === updatedRecord.id,
|
||||||
filter: rootQueryFilter,
|
);
|
||||||
objectMetadataItem,
|
|
||||||
|
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) {
|
if (updatedRecordShouldBeRemovedFromRootQueryEdges) {
|
||||||
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
|
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootQueryNextEdgesShouldBeSorted = isDefined(rootQueryOrderBy);
|
const rootQueryNextEdgesShouldBeSorted = isDefined(rootQueryOrderBy);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ApolloCache } from '@apollo/client';
|
import { ApolloCache } from '@apollo/client';
|
||||||
|
|
||||||
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
|
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 { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||||
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
|
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
@ -122,10 +122,10 @@ export const triggerUpdateRelationsOptimisticEffect = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (shouldCascadeDeleteTargetRecords) {
|
if (shouldCascadeDeleteTargetRecords) {
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem: fullTargetObjectMetadataItem,
|
objectMetadataItem: fullTargetObjectMetadataItem,
|
||||||
recordsToDelete: targetRecordsToDetachFrom,
|
recordsToDestroy: targetRecordsToDetachFrom,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,11 +18,11 @@ export const useDeleteRecordFromCache = ({
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
return (recordToDelete: ObjectRecord) => {
|
return (recordToDestroy: ObjectRecord) => {
|
||||||
deleteRecordFromCache({
|
deleteRecordFromCache({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
recordToDelete,
|
recordToDestroy,
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ApolloCache } from '@apollo/client';
|
import { ApolloCache } from '@apollo/client';
|
||||||
|
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
@ -8,21 +8,21 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|||||||
export const deleteRecordFromCache = ({
|
export const deleteRecordFromCache = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
recordToDelete,
|
recordToDestroy,
|
||||||
cache,
|
cache,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
objectMetadataItems: ObjectMetadataItem[];
|
objectMetadataItems: ObjectMetadataItem[];
|
||||||
recordToDelete: ObjectRecord;
|
recordToDestroy: ObjectRecord;
|
||||||
cache: ApolloCache<object>;
|
cache: ApolloCache<object>;
|
||||||
}) => {
|
}) => {
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
recordsToDelete: [
|
recordsToDestroy: [
|
||||||
{
|
{
|
||||||
...recordToDelete,
|
...recordToDestroy,
|
||||||
__typename: getObjectTypename(objectMetadataItem.nameSingular),
|
__typename: getObjectTypename(objectMetadataItem.nameSingular),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { act, renderHook } from '@testing-library/react';
|
import { renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
query,
|
query,
|
||||||
@ -6,9 +6,10 @@ import {
|
|||||||
variables,
|
variables,
|
||||||
} from '@/object-record/hooks/__mocks__/useDeleteManyRecords';
|
} from '@/object-record/hooks/__mocks__/useDeleteManyRecords';
|
||||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
|
import { act } from 'react';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
const people = [
|
const personIds = [
|
||||||
'a7286b9a-c039-4a89-9567-2dfa7953cda9',
|
'a7286b9a-c039-4a89-9567-2dfa7953cda9',
|
||||||
'37faabcd-cb39-4a0a-8618-7e3fda9afca0',
|
'37faabcd-cb39-4a0a-8618-7e3fda9afca0',
|
||||||
];
|
];
|
||||||
@ -41,7 +42,7 @@ describe('useDeleteManyRecords', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const res = await result.current.deleteManyRecords(people);
|
const res = await result.current.deleteManyRecords(personIds);
|
||||||
expect(res).toBeDefined();
|
expect(res).toBeDefined();
|
||||||
expect(res[0]).toHaveProperty('id');
|
expect(res[0]).toHaveProperty('id');
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@ import { useApolloClient } from '@apollo/client';
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
|
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
|
||||||
@ -122,19 +122,19 @@ export const useCreateManyRecords = <
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
recordsCreatedInCache.forEach((recordToDelete) => {
|
recordsCreatedInCache.forEach((recordToDestroy) => {
|
||||||
deleteRecordFromCache({
|
deleteRecordFromCache({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
recordToDelete,
|
recordToDestroy,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: recordsCreatedInCache,
|
recordsToDestroy: recordsCreatedInCache,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { useState } from 'react';
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
|
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
|
||||||
@ -118,13 +118,13 @@ export const useCreateOneRecord = <
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
recordToDelete: recordCreatedInCache,
|
recordToDestroy: recordCreatedInCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [recordCreatedInCache],
|
recordsToDestroy: [recordCreatedInCache],
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
|
||||||
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||||
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
@ -62,49 +63,74 @@ export const useDeleteManyRecords = ({
|
|||||||
const deletedRecords = [];
|
const deletedRecords = [];
|
||||||
|
|
||||||
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
||||||
const batchIds = idsToDelete.slice(
|
const batchedIdsToDelete = idsToDelete.slice(
|
||||||
batchIndex * mutationPageSize,
|
batchIndex * mutationPageSize,
|
||||||
(batchIndex + 1) * mutationPageSize,
|
(batchIndex + 1) * mutationPageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentTimestamp = new Date().toISOString();
|
||||||
|
|
||||||
|
const cachedRecords = batchedIdsToDelete
|
||||||
|
.map((idToDelete) => getRecordFromCache(idToDelete, apolloClient.cache))
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
|
if (!options?.skipOptimisticEffect) {
|
||||||
|
cachedRecords.forEach((cachedRecord) => {
|
||||||
|
if (!cachedRecord || !cachedRecord.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: cachedRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedOptimisticRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
...{ id: cachedRecord.id, deletedAt: currentTimestamp },
|
||||||
|
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
|
||||||
|
};
|
||||||
|
|
||||||
|
const optimisticRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!optimisticRecordWithConnection || !cachedRecordWithConnection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
});
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: cachedRecordWithConnection,
|
||||||
|
updatedRecord: optimisticRecordWithConnection,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const deletedRecordsResponse = await apolloClient
|
const deletedRecordsResponse = await apolloClient
|
||||||
.mutate({
|
.mutate({
|
||||||
mutation: deleteManyRecordsMutation,
|
mutation: deleteManyRecordsMutation,
|
||||||
variables: {
|
variables: {
|
||||||
filter: { id: { in: batchIds } },
|
filter: { id: { in: batchedIdsToDelete } },
|
||||||
},
|
},
|
||||||
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,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
const cachedRecords = batchIds.map((idToDelete) =>
|
|
||||||
getRecordFromCache(idToDelete, apolloClient.cache),
|
|
||||||
);
|
|
||||||
|
|
||||||
cachedRecords.forEach((cachedRecord) => {
|
cachedRecords.forEach((cachedRecord) => {
|
||||||
if (!cachedRecord) {
|
if (!cachedRecord) {
|
||||||
return;
|
return;
|
||||||
@ -114,23 +140,45 @@ export const useDeleteManyRecords = ({
|
|||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
record: {
|
record: cachedRecord,
|
||||||
...cachedRecord,
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
triggerCreateRecordsOptimisticEffect({
|
const cachedRecordWithConnection =
|
||||||
cache: apolloClient.cache,
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
objectMetadataItem,
|
record: cachedRecord,
|
||||||
objectMetadataItems,
|
objectMetadataItem,
|
||||||
recordsToCreate: cachedRecords
|
objectMetadataItems,
|
||||||
.filter(isDefined)
|
computeReferences: true,
|
||||||
.map((cachedRecord) => ({
|
});
|
||||||
...cachedRecord,
|
|
||||||
deletedAt: null,
|
const computedOptimisticRecord = {
|
||||||
})),
|
...cachedRecord,
|
||||||
|
...{ id: cachedRecord.id, deletedAt: currentTimestamp },
|
||||||
|
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
|
||||||
|
};
|
||||||
|
|
||||||
|
const optimisticRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!optimisticRecordWithConnection ||
|
||||||
|
!cachedRecordWithConnection
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: optimisticRecordWithConnection,
|
||||||
|
updatedRecord: cachedRecordWithConnection,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
@ -41,66 +42,85 @@ export const useDeleteOneRecord = ({
|
|||||||
async (idToDelete: string) => {
|
async (idToDelete: string) => {
|
||||||
const currentTimestamp = new Date().toISOString();
|
const currentTimestamp = new Date().toISOString();
|
||||||
|
|
||||||
|
const cachedRecord = getRecordFromCache(idToDelete, apolloClient.cache);
|
||||||
|
|
||||||
|
const cachedRecordWithConnection = getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: cachedRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedOptimisticRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
...{ id: idToDelete, deletedAt: currentTimestamp },
|
||||||
|
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
|
||||||
|
};
|
||||||
|
|
||||||
|
const optimisticRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!optimisticRecordWithConnection || !cachedRecordWithConnection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
});
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: cachedRecordWithConnection,
|
||||||
|
updatedRecord: optimisticRecordWithConnection,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
|
||||||
const deletedRecord = await apolloClient
|
const deletedRecord = await apolloClient
|
||||||
.mutate({
|
.mutate({
|
||||||
mutation: deleteOneRecordMutation,
|
mutation: deleteOneRecordMutation,
|
||||||
variables: {
|
variables: {
|
||||||
idToDelete: idToDelete,
|
idToDelete: idToDelete,
|
||||||
},
|
},
|
||||||
optimisticResponse: {
|
|
||||||
[mutationResponseField]: {
|
|
||||||
__typename: capitalize(objectNameSingular),
|
|
||||||
id: idToDelete,
|
|
||||||
deletedAt: currentTimestamp,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.[mutationResponseField];
|
const record = data?.[mutationResponseField];
|
||||||
|
|
||||||
if (!record) return;
|
if (!record || !cachedRecord) return;
|
||||||
|
|
||||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
currentRecord: cachedRecord,
|
||||||
|
updatedRecord: record,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
const cachedRecord = getRecordFromCache(
|
|
||||||
idToDelete,
|
|
||||||
apolloClient.cache,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cachedRecord) {
|
if (!cachedRecord) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRecordFromCache({
|
updateRecordFromCache({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
record: {
|
record: cachedRecord,
|
||||||
...cachedRecord,
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
triggerCreateRecordsOptimisticEffect({
|
triggerUpdateRecordOptimisticEffect({
|
||||||
cache: apolloClient.cache,
|
cache: apolloClient.cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
|
currentRecord: optimisticRecordWithConnection,
|
||||||
|
updatedRecord: cachedRecordWithConnection,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
recordsToCreate: [
|
|
||||||
{
|
|
||||||
...cachedRecord,
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
@ -114,7 +134,6 @@ export const useDeleteOneRecord = ({
|
|||||||
getRecordFromCache,
|
getRecordFromCache,
|
||||||
mutationResponseField,
|
mutationResponseField,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectNameSingular,
|
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
@ -61,12 +61,12 @@ export const useDestroyManyRecords = ({
|
|||||||
const destroyedRecords = [];
|
const destroyedRecords = [];
|
||||||
|
|
||||||
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
||||||
const batchIds = idsToDestroy.slice(
|
const batchedIdToDestroy = idsToDestroy.slice(
|
||||||
batchIndex * mutationPageSize,
|
batchIndex * mutationPageSize,
|
||||||
(batchIndex + 1) * mutationPageSize,
|
(batchIndex + 1) * mutationPageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
const originalRecords = idsToDestroy
|
const originalRecords = batchedIdToDestroy
|
||||||
.map((recordId) => getRecordFromCache(recordId, apolloClient.cache))
|
.map((recordId) => getRecordFromCache(recordId, apolloClient.cache))
|
||||||
.filter(isDefined);
|
.filter(isDefined);
|
||||||
|
|
||||||
@ -74,15 +74,17 @@ export const useDestroyManyRecords = ({
|
|||||||
.mutate({
|
.mutate({
|
||||||
mutation: destroyManyRecordsMutation,
|
mutation: destroyManyRecordsMutation,
|
||||||
variables: {
|
variables: {
|
||||||
filter: { id: { in: batchIds } },
|
filter: { id: { in: batchedIdToDestroy } },
|
||||||
},
|
},
|
||||||
optimisticResponse: options?.skipOptimisticEffect
|
optimisticResponse: options?.skipOptimisticEffect
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
[mutationResponseField]: batchIds.map((idToDestroy) => ({
|
[mutationResponseField]: batchedIdToDestroy.map(
|
||||||
__typename: capitalize(objectNameSingular),
|
(idToDestroy) => ({
|
||||||
id: idToDestroy,
|
__typename: capitalize(objectNameSingular),
|
||||||
})),
|
id: idToDestroy,
|
||||||
|
}),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
update: options?.skipOptimisticEffect
|
update: options?.skipOptimisticEffect
|
||||||
? undefined
|
? undefined
|
||||||
@ -95,10 +97,10 @@ export const useDestroyManyRecords = ({
|
|||||||
.map((record) => getRecordFromCache(record.id, cache))
|
.map((record) => getRecordFromCache(record.id, cache))
|
||||||
.filter(isDefined);
|
.filter(isDefined);
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: cachedRecords,
|
recordsToDestroy: cachedRecords,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import { useApolloClient } from '@apollo/client';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
@ -65,10 +65,10 @@ export const useDestroyOneRecord = ({
|
|||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
recordsToDestroy: [cachedRecord],
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||||
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||||
import { useRestoreManyRecordsMutation } from '@/object-record/hooks/useRestoreManyRecordsMutation';
|
import { useRestoreManyRecordsMutation } from '@/object-record/hooks/useRestoreManyRecordsMutation';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getRestoreManyRecordsMutationResponseField } from '@/object-record/utils/getRestoreManyRecordsMutationResponseField';
|
import { getRestoreManyRecordsMutationResponseField } from '@/object-record/utils/getRestoreManyRecordsMutationResponseField';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
@ -34,10 +40,16 @@ export const useRestoreManyRecords = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getRecordFromCache = useGetRecordFromCache({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
const { restoreManyRecordsMutation } = useRestoreManyRecordsMutation({
|
const { restoreManyRecordsMutation } = useRestoreManyRecordsMutation({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
const mutationResponseField = getRestoreManyRecordsMutationResponseField(
|
const mutationResponseField = getRestoreManyRecordsMutationResponseField(
|
||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
);
|
);
|
||||||
@ -51,36 +63,124 @@ export const useRestoreManyRecords = ({
|
|||||||
const restoredRecords = [];
|
const restoredRecords = [];
|
||||||
|
|
||||||
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
for (let batchIndex = 0; batchIndex < numberOfBatches; batchIndex++) {
|
||||||
const batchIds = idsToRestore.slice(
|
const batchedIdsToRestore = idsToRestore.slice(
|
||||||
batchIndex * mutationPageSize,
|
batchIndex * mutationPageSize,
|
||||||
(batchIndex + 1) * mutationPageSize,
|
(batchIndex + 1) * mutationPageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: fix optimistic effect
|
const cachedRecords = batchedIdsToRestore
|
||||||
const findOneQueryName = `FindOne${capitalize(objectNameSingular)}`;
|
.map((idToRestore) =>
|
||||||
const findManyQueryName = `FindMany${capitalize(
|
getRecordFromCache(idToRestore, apolloClient.cache),
|
||||||
objectMetadataItem.namePlural,
|
)
|
||||||
)}`;
|
.filter(isDefined);
|
||||||
|
|
||||||
|
if (!options?.skipOptimisticEffect) {
|
||||||
|
cachedRecords.forEach((cachedRecord) => {
|
||||||
|
if (!cachedRecord || !cachedRecord.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: cachedRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedOptimisticRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
...{ id: cachedRecord.id, deletedAt: null },
|
||||||
|
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
|
||||||
|
};
|
||||||
|
|
||||||
|
const optimisticRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!optimisticRecordWithConnection || !cachedRecordWithConnection) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
});
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: cachedRecordWithConnection,
|
||||||
|
updatedRecord: optimisticRecordWithConnection,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const restoredRecordsResponse = await apolloClient
|
const restoredRecordsResponse = await apolloClient
|
||||||
.mutate({
|
.mutate({
|
||||||
mutation: restoreManyRecordsMutation,
|
mutation: restoreManyRecordsMutation,
|
||||||
refetchQueries: [findOneQueryName, findManyQueryName],
|
|
||||||
variables: {
|
variables: {
|
||||||
filter: { id: { in: batchIds } },
|
filter: { id: { in: batchedIdsToRestore } },
|
||||||
},
|
},
|
||||||
optimisticResponse: options?.skipOptimisticEffect
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
[mutationResponseField]: batchIds.map((idToRestore) => ({
|
|
||||||
__typename: capitalize(objectNameSingular),
|
|
||||||
id: idToRestore,
|
|
||||||
deletedAt: null,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
// TODO: revert optimistic effect (once optimistic effect is fixed)
|
cachedRecords.forEach((cachedRecord) => {
|
||||||
|
if (!cachedRecord) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: cachedRecord,
|
||||||
|
});
|
||||||
|
|
||||||
|
const cachedRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: cachedRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedOptimisticRecord = {
|
||||||
|
...cachedRecord,
|
||||||
|
...{ id: cachedRecord.id, deletedAt: null },
|
||||||
|
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
|
||||||
|
};
|
||||||
|
|
||||||
|
const optimisticRecordWithConnection =
|
||||||
|
getRecordNodeFromRecord<ObjectRecord>({
|
||||||
|
record: computedOptimisticRecord,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
computeReferences: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!optimisticRecordWithConnection ||
|
||||||
|
!cachedRecordWithConnection
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: optimisticRecordWithConnection,
|
||||||
|
updatedRecord: cachedRecordWithConnection,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
EmailsFilter,
|
EmailsFilter,
|
||||||
FloatFilter,
|
FloatFilter,
|
||||||
FullNameFilter,
|
FullNameFilter,
|
||||||
|
LeafObjectRecordFilter,
|
||||||
LinksFilter,
|
LinksFilter,
|
||||||
NotObjectRecordFilter,
|
NotObjectRecordFilter,
|
||||||
OrObjectRecordFilter,
|
OrObjectRecordFilter,
|
||||||
@ -29,6 +30,12 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
import { isEmptyObject } from '~/utils/isEmptyObject';
|
||||||
|
|
||||||
|
const isLeafFilter = (
|
||||||
|
filter: RecordGqlOperationFilter,
|
||||||
|
): filter is LeafObjectRecordFilter => {
|
||||||
|
return !isAndFilter(filter) && !isOrFilter(filter) && !isNotFilter(filter);
|
||||||
|
};
|
||||||
|
|
||||||
const isAndFilter = (
|
const isAndFilter = (
|
||||||
filter: RecordGqlOperationFilter,
|
filter: RecordGqlOperationFilter,
|
||||||
): filter is AndObjectRecordFilter => 'and' in filter && !!filter.and;
|
): filter is AndObjectRecordFilter => 'and' in filter && !!filter.and;
|
||||||
@ -50,7 +57,7 @@ export const isRecordMatchingFilter = ({
|
|||||||
filter: RecordGqlOperationFilter;
|
filter: RecordGqlOperationFilter;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}): boolean => {
|
}): boolean => {
|
||||||
if (Object.keys(filter).length === 0) {
|
if (Object.keys(filter).length === 0 && record.deletedAt === null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +127,12 @@ export const isRecordMatchingFilter = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLeafFilter(filter)) {
|
||||||
|
if (isDefined(record.deletedAt) && filter.deletedAt === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Object.entries(filter).every(([filterKey, filterValue]) => {
|
return Object.entries(filter).every(([filterKey, filterValue]) => {
|
||||||
if (!isDefined(filterValue)) {
|
if (!isDefined(filterValue)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
import { useFetchAllRecordIds } from '@/object-record/hooks/useFetchAllRecordIds';
|
|
||||||
import { UseTableDataOptions } from '@/object-record/record-index/options/hooks/useTableData';
|
import { UseTableDataOptions } from '@/object-record/record-index/options/hooks/useTableData';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
|
|
||||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
type UseDeleteTableDataOptions = Pick<
|
type UseDeleteTableDataOptions = Pick<
|
||||||
UseTableDataOptions,
|
UseTableDataOptions,
|
||||||
@ -16,41 +12,16 @@ export const useDeleteTableData = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
recordIndexId,
|
recordIndexId,
|
||||||
}: UseDeleteTableDataOptions) => {
|
}: UseDeleteTableDataOptions) => {
|
||||||
const { fetchAllRecordIds } = useFetchAllRecordIds({
|
const { resetTableRowSelection } = useRecordTable({
|
||||||
objectNameSingular,
|
recordTableId: recordIndexId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { resetTableRowSelection, hasUserSelectedAllRowsState } =
|
|
||||||
useRecordTable({
|
|
||||||
recordTableId: recordIndexId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableRowIds = useRecoilValue(
|
|
||||||
tableRowIdsComponentState({
|
|
||||||
scopeId: getScopeIdFromComponentId(recordIndexId),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const { deleteManyRecords } = useDeleteManyRecords({
|
const { deleteManyRecords } = useDeleteManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
const { favorites, deleteFavorite } = useFavorites();
|
const { favorites, deleteFavorite } = useFavorites();
|
||||||
|
|
||||||
const hasUserSelectedAllRows = useRecoilValue(hasUserSelectedAllRowsState);
|
|
||||||
|
|
||||||
const deleteRecords = async (recordIdsToDelete: string[]) => {
|
const deleteRecords = async (recordIdsToDelete: string[]) => {
|
||||||
if (hasUserSelectedAllRows) {
|
|
||||||
const allRecordIds = await fetchAllRecordIds();
|
|
||||||
|
|
||||||
const unselectedRecordIds = tableRowIds.filter(
|
|
||||||
(recordId) => !recordIdsToDelete.includes(recordId),
|
|
||||||
);
|
|
||||||
|
|
||||||
recordIdsToDelete = allRecordIds.filter(
|
|
||||||
(recordId) => !unselectedRecordIds.includes(recordId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetTableRowSelection();
|
resetTableRowSelection();
|
||||||
|
|
||||||
for (const recordIdToDelete of recordIdsToDelete) {
|
for (const recordIdToDelete of recordIdsToDelete) {
|
||||||
|
@ -11,7 +11,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||||
|
|
||||||
import { useDestroyManyRecords } from '@/object-record/hooks/useDestroyManyRecords';
|
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
|
||||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { Dropdown } from '../../dropdown/components/Dropdown';
|
import { Dropdown } from '../../dropdown/components/Dropdown';
|
||||||
@ -35,7 +35,7 @@ export const ShowPageMoreButton = ({
|
|||||||
const { deleteOneRecord } = useDeleteOneRecord({
|
const { deleteOneRecord } = useDeleteOneRecord({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
const { destroyManyRecords } = useDestroyManyRecords({
|
const { destroyOneRecord } = useDestroyOneRecord({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
const { restoreManyRecords } = useRestoreManyRecords({
|
const { restoreManyRecords } = useRestoreManyRecords({
|
||||||
@ -48,7 +48,7 @@ export const ShowPageMoreButton = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDestroy = () => {
|
const handleDestroy = () => {
|
||||||
destroyManyRecords([recordId]);
|
destroyOneRecord(recordId);
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
navigate(navigationMemorizedUrl, { replace: true });
|
navigate(navigationMemorizedUrl, { replace: true });
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
@ -24,7 +24,7 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { deleteOneRecordMutation } = useDeleteOneRecordMutation({
|
const { destroyOneRecordMutation } = useDestroyOneRecordMutation({
|
||||||
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -129,12 +129,12 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
viewFilterIdsToDelete.map((viewFilterId) =>
|
viewFilterIdsToDelete.map((viewFilterId) =>
|
||||||
apolloClient.mutate({
|
apolloClient.mutate({
|
||||||
mutation: deleteOneRecordMutation,
|
mutation: destroyOneRecordMutation,
|
||||||
variables: {
|
variables: {
|
||||||
idToDelete: viewFilterId,
|
idToDestroy: viewFilterId,
|
||||||
},
|
},
|
||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.['deleteViewFilter'];
|
const record = data?.['destroyViewFilter'];
|
||||||
|
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
@ -142,10 +142,10 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
recordsToDestroy: [cachedRecord],
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -155,7 +155,7 @@ export const usePersistViewFilterRecords = () => {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
apolloClient,
|
apolloClient,
|
||||||
deleteOneRecordMutation,
|
destroyOneRecordMutation,
|
||||||
getRecordFromCache,
|
getRecordFromCache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
|
||||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
@ -24,7 +24,7 @@ export const usePersistViewSortRecords = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { deleteOneRecordMutation } = useDeleteOneRecordMutation({
|
const { destroyOneRecordMutation } = useDestroyOneRecordMutation({
|
||||||
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -124,12 +124,12 @@ export const usePersistViewSortRecords = () => {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
viewSortIdsToDelete.map((viewSortId) =>
|
viewSortIdsToDelete.map((viewSortId) =>
|
||||||
apolloClient.mutate({
|
apolloClient.mutate({
|
||||||
mutation: deleteOneRecordMutation,
|
mutation: destroyOneRecordMutation,
|
||||||
variables: {
|
variables: {
|
||||||
idToDelete: viewSortId,
|
idToDestroy: viewSortId,
|
||||||
},
|
},
|
||||||
update: (cache, { data }) => {
|
update: (cache, { data }) => {
|
||||||
const record = data?.['deleteViewSort'];
|
const record = data?.['destroyViewSort'];
|
||||||
|
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
@ -137,10 +137,10 @@ export const usePersistViewSortRecords = () => {
|
|||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDestroyRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
recordsToDestroy: [cachedRecord],
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -150,7 +150,7 @@ export const usePersistViewSortRecords = () => {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
apolloClient,
|
apolloClient,
|
||||||
deleteOneRecordMutation,
|
destroyOneRecordMutation,
|
||||||
getRecordFromCache,
|
getRecordFromCache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
|
@ -82,62 +82,62 @@ export class GraphqlQueryFilterFieldParser {
|
|||||||
switch (operator) {
|
switch (operator) {
|
||||||
case 'eq':
|
case 'eq':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} = :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" = :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'neq':
|
case 'neq':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} != :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" != :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'gt':
|
case 'gt':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} > :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" > :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'gte':
|
case 'gte':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} >= :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" >= :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'lt':
|
case 'lt':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} < :${key}${uuid}`,
|
sql: `"${objectNameSingular}".${key} < :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'lte':
|
case 'lte':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} <= :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" <= :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'in':
|
case 'in':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} IN (:...${key}${uuid})`,
|
sql: `"${objectNameSingular}"."${key}" IN (:...${key}${uuid})`,
|
||||||
params: { [`${key}${uuid}`]: value },
|
params: { [`${key}${uuid}`]: value },
|
||||||
};
|
};
|
||||||
case 'is':
|
case 'is':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
|
sql: `"${objectNameSingular}"."${key}" IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
|
||||||
params: {},
|
params: {},
|
||||||
};
|
};
|
||||||
case 'like':
|
case 'like':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: `${value}` },
|
params: { [`${key}${uuid}`]: `${value}` },
|
||||||
};
|
};
|
||||||
case 'ilike':
|
case 'ilike':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} ILIKE :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" ILIKE :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: `${value}` },
|
params: { [`${key}${uuid}`]: `${value}` },
|
||||||
};
|
};
|
||||||
case 'startsWith':
|
case 'startsWith':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: `${value}` },
|
params: { [`${key}${uuid}`]: `${value}` },
|
||||||
};
|
};
|
||||||
case 'endsWith':
|
case 'endsWith':
|
||||||
return {
|
return {
|
||||||
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
|
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
|
||||||
params: { [`${key}${uuid}`]: `${value}` },
|
params: { [`${key}${uuid}`]: `${value}` },
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
|
@ -58,7 +58,7 @@ export class GraphqlQueryDestroyOneResolverService
|
|||||||
);
|
);
|
||||||
|
|
||||||
const nonFormattedDeletedObjectRecords = await queryBuilder
|
const nonFormattedDeletedObjectRecords = await queryBuilder
|
||||||
.where({
|
.where(`"${objectMetadataMapItem.nameSingular}".id = :id`, {
|
||||||
id: args.id,
|
id: args.id,
|
||||||
})
|
})
|
||||||
.take(1)
|
.take(1)
|
||||||
|
@ -16,6 +16,7 @@ import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/obj
|
|||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
|
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GraphqlQueryUpdateManyResolverService
|
export class GraphqlQueryUpdateManyResolverService
|
||||||
@ -57,9 +58,14 @@ export class GraphqlQueryUpdateManyResolverService
|
|||||||
objectMetadataMapItem.nameSingular,
|
objectMetadataMapItem.nameSingular,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const tableName = computeTableName(
|
||||||
|
objectMetadataMapItem.nameSingular,
|
||||||
|
objectMetadataMapItem.isCustom,
|
||||||
|
);
|
||||||
|
|
||||||
const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder(
|
const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder(
|
||||||
queryBuilder,
|
queryBuilder,
|
||||||
objectMetadataMapItem.nameSingular,
|
tableName,
|
||||||
args.filter,
|
args.filter,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user