Revert optimistic rendering on negative response (#7541)
Fixes #7299 The changes primarily focus on ensuring that records are correctly handled in the cache and optimistic effects are reverted appropriately when mutations fail.
This commit is contained in:
@ -2,9 +2,11 @@ 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 { 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';
|
||||||
|
import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordFromCache';
|
||||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
@ -67,7 +69,7 @@ export const useCreateManyRecords = <
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const recordsCreatedInCache = [];
|
const recordsCreatedInCache: ObjectRecord[] = [];
|
||||||
|
|
||||||
for (const recordToCreate of sanitizedCreateManyRecordsInput) {
|
for (const recordToCreate of sanitizedCreateManyRecordsInput) {
|
||||||
if (recordToCreate.id === null) {
|
if (recordToCreate.id === null) {
|
||||||
@ -98,26 +100,46 @@ export const useCreateManyRecords = <
|
|||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
);
|
);
|
||||||
|
|
||||||
const createdObjects = await apolloClient.mutate({
|
const createdObjects = await apolloClient
|
||||||
mutation: createManyRecordsMutation,
|
.mutate({
|
||||||
variables: {
|
mutation: createManyRecordsMutation,
|
||||||
data: sanitizedCreateManyRecordsInput,
|
variables: {
|
||||||
upsert: upsert,
|
data: sanitizedCreateManyRecordsInput,
|
||||||
},
|
upsert: upsert,
|
||||||
update: (cache, { data }) => {
|
},
|
||||||
const records = data?.[mutationResponseField];
|
update: (cache, { data }) => {
|
||||||
|
const records = data?.[mutationResponseField];
|
||||||
|
|
||||||
if (!records?.length || skipPostOptmisticEffect) return;
|
if (!records?.length || skipPostOptmisticEffect) return;
|
||||||
|
|
||||||
triggerCreateRecordsOptimisticEffect({
|
triggerCreateRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToCreate: records,
|
recordsToCreate: records,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
shouldMatchRootQueryFilter,
|
shouldMatchRootQueryFilter,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
recordsCreatedInCache.forEach((recordToDelete) => {
|
||||||
|
deleteRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
recordToDelete,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
triggerDeleteRecordsOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordsToDelete: recordsCreatedInCache,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
return createdObjects.data?.[mutationResponseField] ?? [];
|
return createdObjects.data?.[mutationResponseField] ?? [];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,9 +3,11 @@ 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 { 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';
|
||||||
|
import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordFromCache';
|
||||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
@ -85,27 +87,49 @@ export const useCreateOneRecord = <
|
|||||||
const mutationResponseField =
|
const mutationResponseField =
|
||||||
getCreateOneRecordMutationResponseField(objectNameSingular);
|
getCreateOneRecordMutationResponseField(objectNameSingular);
|
||||||
|
|
||||||
const createdObject = await apolloClient.mutate({
|
const createdObject = await apolloClient
|
||||||
mutation: createOneRecordMutation,
|
.mutate({
|
||||||
variables: {
|
mutation: createOneRecordMutation,
|
||||||
input: sanitizedInput,
|
variables: {
|
||||||
},
|
input: sanitizedInput,
|
||||||
update: (cache, { data }) => {
|
},
|
||||||
const record = data?.[mutationResponseField];
|
update: (cache, { data }) => {
|
||||||
|
const record = data?.[mutationResponseField];
|
||||||
|
|
||||||
if (!record || skipPostOptmisticEffect) return;
|
if (!record || skipPostOptmisticEffect) return;
|
||||||
|
|
||||||
triggerCreateRecordsOptimisticEffect({
|
triggerCreateRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToCreate: [record],
|
recordsToCreate: [record],
|
||||||
|
objectMetadataItems,
|
||||||
|
shouldMatchRootQueryFilter,
|
||||||
|
});
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
if (!recordCreatedInCache) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRecordFromCache({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
shouldMatchRootQueryFilter,
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
recordToDelete: recordCreatedInCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
setLoading(false);
|
triggerDeleteRecordsOptimisticEffect({
|
||||||
},
|
cache: apolloClient.cache,
|
||||||
});
|
objectMetadataItem,
|
||||||
|
recordsToDelete: [recordCreatedInCache],
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
return createdObject.data?.[mutationResponseField] ?? null;
|
return createdObject.data?.[mutationResponseField] ?? null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
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 { 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 { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||||
@ -65,38 +67,74 @@ export const useDeleteManyRecords = ({
|
|||||||
(batchIndex + 1) * mutationPageSize,
|
(batchIndex + 1) * mutationPageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
const deletedRecordsResponse = await apolloClient.mutate({
|
const deletedRecordsResponse = await apolloClient
|
||||||
mutation: deleteManyRecordsMutation,
|
.mutate({
|
||||||
variables: {
|
mutation: deleteManyRecordsMutation,
|
||||||
filter: { id: { in: batchIds } },
|
variables: {
|
||||||
},
|
filter: { id: { in: batchIds } },
|
||||||
optimisticResponse: options?.skipOptimisticEffect
|
},
|
||||||
? undefined
|
optimisticResponse: options?.skipOptimisticEffect
|
||||||
: {
|
? undefined
|
||||||
[mutationResponseField]: batchIds.map((idToDelete) => ({
|
: {
|
||||||
__typename: capitalize(objectNameSingular),
|
[mutationResponseField]: batchIds.map((idToDelete) => ({
|
||||||
id: 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) => {
|
||||||
|
const cachedRecords = batchIds.map((idToDelete) =>
|
||||||
|
getRecordFromCache(idToDelete, apolloClient.cache),
|
||||||
|
);
|
||||||
|
|
||||||
|
cachedRecords.forEach((cachedRecord) => {
|
||||||
|
if (!cachedRecord) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: {
|
||||||
|
...cachedRecord,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
triggerCreateRecordsOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
recordsToCreate: cachedRecords
|
||||||
|
.filter(isDefined)
|
||||||
|
.map((cachedRecord) => ({
|
||||||
|
...cachedRecord,
|
||||||
|
deletedAt: null,
|
||||||
})),
|
})),
|
||||||
},
|
});
|
||||||
update: options?.skipOptimisticEffect
|
|
||||||
? undefined
|
|
||||||
: (cache, { data }) => {
|
|
||||||
const records = data?.[mutationResponseField];
|
|
||||||
|
|
||||||
if (!records?.length) return;
|
throw error;
|
||||||
|
});
|
||||||
const cachedRecords = records
|
|
||||||
.map((record) => getRecordFromCache(record.id, cache))
|
|
||||||
.filter(isDefined);
|
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
|
||||||
cache,
|
|
||||||
objectMetadataItem,
|
|
||||||
recordsToDelete: cachedRecords,
|
|
||||||
objectMetadataItems,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const deletedRecordsForThisBatch =
|
const deletedRecordsForThisBatch =
|
||||||
deletedRecordsResponse.data?.[mutationResponseField] ?? [];
|
deletedRecordsResponse.data?.[mutationResponseField] ?? [];
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
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 { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
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 { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||||
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';
|
||||||
@ -39,35 +41,70 @@ export const useDeleteOneRecord = ({
|
|||||||
async (idToDelete: string) => {
|
async (idToDelete: string) => {
|
||||||
const currentTimestamp = new Date().toISOString();
|
const currentTimestamp = new Date().toISOString();
|
||||||
|
|
||||||
const deletedRecord = await apolloClient.mutate({
|
const deletedRecord = await apolloClient
|
||||||
mutation: deleteOneRecordMutation,
|
.mutate({
|
||||||
variables: {
|
mutation: deleteOneRecordMutation,
|
||||||
idToDelete: idToDelete,
|
variables: {
|
||||||
},
|
idToDelete: idToDelete,
|
||||||
optimisticResponse: {
|
|
||||||
[mutationResponseField]: {
|
|
||||||
__typename: capitalize(objectNameSingular),
|
|
||||||
id: idToDelete,
|
|
||||||
deletedAt: currentTimestamp,
|
|
||||||
},
|
},
|
||||||
},
|
optimisticResponse: {
|
||||||
update: (cache, { data }) => {
|
[mutationResponseField]: {
|
||||||
const record = data?.[mutationResponseField];
|
__typename: capitalize(objectNameSingular),
|
||||||
|
id: idToDelete,
|
||||||
|
deletedAt: currentTimestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: (cache, { data }) => {
|
||||||
|
const record = data?.[mutationResponseField];
|
||||||
|
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDeleteRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
recordsToDelete: [cachedRecord],
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
const cachedRecord = getRecordFromCache(
|
||||||
|
idToDelete,
|
||||||
|
apolloClient.cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!cachedRecord) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecordFromCache({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: {
|
||||||
|
...cachedRecord,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
triggerCreateRecordsOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
recordsToCreate: [
|
||||||
|
{
|
||||||
|
...cachedRecord,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
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';
|
||||||
@ -65,38 +66,54 @@ export const useDestroyManyRecords = ({
|
|||||||
(batchIndex + 1) * mutationPageSize,
|
(batchIndex + 1) * mutationPageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
const destroyedRecordsResponse = await apolloClient.mutate({
|
const originalRecords = idsToDestroy
|
||||||
mutation: destroyManyRecordsMutation,
|
.map((recordId) => getRecordFromCache(recordId, apolloClient.cache))
|
||||||
variables: {
|
.filter(isDefined);
|
||||||
filter: { id: { in: batchIds } },
|
|
||||||
},
|
|
||||||
optimisticResponse: options?.skipOptimisticEffect
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
[mutationResponseField]: batchIds.map((idToDestroy) => ({
|
|
||||||
__typename: capitalize(objectNameSingular),
|
|
||||||
id: idToDestroy,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
update: options?.skipOptimisticEffect
|
|
||||||
? undefined
|
|
||||||
: (cache, { data }) => {
|
|
||||||
const records = data?.[mutationResponseField];
|
|
||||||
|
|
||||||
if (!records?.length) return;
|
const destroyedRecordsResponse = await apolloClient
|
||||||
|
.mutate({
|
||||||
|
mutation: destroyManyRecordsMutation,
|
||||||
|
variables: {
|
||||||
|
filter: { id: { in: batchIds } },
|
||||||
|
},
|
||||||
|
optimisticResponse: options?.skipOptimisticEffect
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
[mutationResponseField]: batchIds.map((idToDestroy) => ({
|
||||||
|
__typename: capitalize(objectNameSingular),
|
||||||
|
id: idToDestroy,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
update: options?.skipOptimisticEffect
|
||||||
|
? undefined
|
||||||
|
: (cache, { data }) => {
|
||||||
|
const records = data?.[mutationResponseField];
|
||||||
|
|
||||||
const cachedRecords = records
|
if (!records?.length) return;
|
||||||
.map((record) => getRecordFromCache(record.id, cache))
|
|
||||||
.filter(isDefined);
|
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
const cachedRecords = records
|
||||||
cache,
|
.map((record) => getRecordFromCache(record.id, cache))
|
||||||
objectMetadataItem,
|
.filter(isDefined);
|
||||||
recordsToDelete: cachedRecords,
|
|
||||||
objectMetadataItems,
|
triggerDeleteRecordsOptimisticEffect({
|
||||||
});
|
cache,
|
||||||
},
|
objectMetadataItem,
|
||||||
});
|
recordsToDelete: cachedRecords,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
if (originalRecords.length > 0) {
|
||||||
|
triggerCreateRecordsOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordsToCreate: originalRecords,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
const destroyedRecordsForThisBatch =
|
const destroyedRecordsForThisBatch =
|
||||||
destroyedRecordsResponse.data?.[mutationResponseField] ?? [];
|
destroyedRecordsResponse.data?.[mutationResponseField] ?? [];
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
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 { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
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 { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getDestroyOneRecordMutationResponseField } from '@/object-record/utils/getDestroyOneRecordMutationResponseField';
|
import { getDestroyOneRecordMutationResponseField } from '@/object-record/utils/getDestroyOneRecordMutationResponseField';
|
||||||
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
type useDestroyOneRecordProps = {
|
type useDestroyOneRecordProps = {
|
||||||
@ -38,32 +41,49 @@ export const useDestroyOneRecord = ({
|
|||||||
|
|
||||||
const destroyOneRecord = useCallback(
|
const destroyOneRecord = useCallback(
|
||||||
async (idToDestroy: string) => {
|
async (idToDestroy: string) => {
|
||||||
const deletedRecord = await apolloClient.mutate({
|
const originalRecord: ObjectRecord | null = getRecordFromCache(
|
||||||
mutation: destroyOneRecordMutation,
|
idToDestroy,
|
||||||
variables: { idToDestroy },
|
apolloClient.cache,
|
||||||
optimisticResponse: {
|
);
|
||||||
[mutationResponseField]: {
|
|
||||||
__typename: capitalize(objectNameSingular),
|
const deletedRecord = await apolloClient
|
||||||
id: idToDestroy,
|
.mutate({
|
||||||
|
mutation: destroyOneRecordMutation,
|
||||||
|
variables: { idToDestroy },
|
||||||
|
optimisticResponse: {
|
||||||
|
[mutationResponseField]: {
|
||||||
|
__typename: capitalize(objectNameSingular),
|
||||||
|
id: idToDestroy,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
update: (cache, { data }) => {
|
||||||
update: (cache, { data }) => {
|
const record = data?.[mutationResponseField];
|
||||||
const record = data?.[mutationResponseField];
|
|
||||||
|
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||||
|
|
||||||
if (!cachedRecord) return;
|
if (!cachedRecord) return;
|
||||||
|
|
||||||
triggerDeleteRecordsOptimisticEffect({
|
triggerDeleteRecordsOptimisticEffect({
|
||||||
cache,
|
cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
recordsToDelete: [cachedRecord],
|
recordsToDelete: [cachedRecord],
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
if (!isUndefinedOrNull(originalRecord)) {
|
||||||
|
triggerCreateRecordsOptimisticEffect({
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordsToCreate: [originalRecord],
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -62,22 +62,27 @@ export const useRestoreManyRecords = ({
|
|||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
const restoredRecordsResponse = await apolloClient.mutate({
|
const restoredRecordsResponse = await apolloClient
|
||||||
mutation: restoreManyRecordsMutation,
|
.mutate({
|
||||||
refetchQueries: [findOneQueryName, findManyQueryName],
|
mutation: restoreManyRecordsMutation,
|
||||||
variables: {
|
refetchQueries: [findOneQueryName, findManyQueryName],
|
||||||
filter: { id: { in: batchIds } },
|
variables: {
|
||||||
},
|
filter: { id: { in: batchIds } },
|
||||||
optimisticResponse: options?.skipOptimisticEffect
|
},
|
||||||
? undefined
|
optimisticResponse: options?.skipOptimisticEffect
|
||||||
: {
|
? undefined
|
||||||
[mutationResponseField]: batchIds.map((idToRestore) => ({
|
: {
|
||||||
__typename: capitalize(objectNameSingular),
|
[mutationResponseField]: batchIds.map((idToRestore) => ({
|
||||||
id: idToRestore,
|
__typename: capitalize(objectNameSingular),
|
||||||
deletedAt: null,
|
id: idToRestore,
|
||||||
})),
|
deletedAt: null,
|
||||||
},
|
})),
|
||||||
});
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
// TODO: revert optimistic effect (once optimistic effect is fixed)
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
const restoredRecordsForThisBatch =
|
const restoredRecordsForThisBatch =
|
||||||
restoredRecordsResponse.data?.[mutationResponseField] ?? [];
|
restoredRecordsResponse.data?.[mutationResponseField] ?? [];
|
||||||
|
|||||||
@ -108,26 +108,48 @@ export const useUpdateOneRecord = <
|
|||||||
const mutationResponseField =
|
const mutationResponseField =
|
||||||
getUpdateOneRecordMutationResponseField(objectNameSingular);
|
getUpdateOneRecordMutationResponseField(objectNameSingular);
|
||||||
|
|
||||||
const updatedRecord = await apolloClient.mutate({
|
const updatedRecord = await apolloClient
|
||||||
mutation: updateOneRecordMutation,
|
.mutate({
|
||||||
variables: {
|
mutation: updateOneRecordMutation,
|
||||||
idToUpdate,
|
variables: {
|
||||||
input: sanitizedInput,
|
idToUpdate,
|
||||||
},
|
input: sanitizedInput,
|
||||||
update: (cache, { data }) => {
|
},
|
||||||
const record = data?.[mutationResponseField];
|
update: (cache, { data }) => {
|
||||||
|
const record = data?.[mutationResponseField];
|
||||||
|
|
||||||
if (!record || !cachedRecord) return;
|
if (!record || !cachedRecord) return;
|
||||||
|
|
||||||
|
triggerUpdateRecordOptimisticEffect({
|
||||||
|
cache,
|
||||||
|
objectMetadataItem,
|
||||||
|
currentRecord: cachedRecord,
|
||||||
|
updatedRecord: record,
|
||||||
|
objectMetadataItems,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
if (!cachedRecord) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
updateRecordFromCache({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
cache: apolloClient.cache,
|
||||||
|
record: cachedRecord,
|
||||||
|
});
|
||||||
|
|
||||||
triggerUpdateRecordOptimisticEffect({
|
triggerUpdateRecordOptimisticEffect({
|
||||||
cache,
|
cache: apolloClient.cache,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
currentRecord: cachedRecord,
|
currentRecord: optimisticRecordWithConnection,
|
||||||
updatedRecord: record,
|
updatedRecord: cachedRecordWithConnection,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user