Feat/record optimistic effect (#3076)
* WIP * WIP * POC working on hard coded completedAt field * Finished isRecordMatchingFilter, mock of pg_graphql filtering mechanism * Fixed and cleaned * Unregister unused optimistic effects * Fix lint * Fixes from review --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,68 +1,109 @@
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { produce } from 'immer';
|
||||
|
||||
import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';
|
||||
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const getRecordOptimisticEffectDefinition = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) =>
|
||||
({
|
||||
key: `record-create-optimistic-effect-definition-${objectMetadataItem.nameSingular}`,
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
resolver: ({
|
||||
currentData,
|
||||
newData,
|
||||
deletedRecordIds,
|
||||
}: {
|
||||
currentData: unknown;
|
||||
newData: { id: string } & Record<string, any>;
|
||||
deletedRecordIds?: string[];
|
||||
}) => {
|
||||
const newRecordPaginatedCacheField = produce<
|
||||
PaginatedRecordTypeResults<any>
|
||||
>(currentData as PaginatedRecordTypeResults<any>, (draft) => {
|
||||
if (newData) {
|
||||
if (!draft) {
|
||||
return {
|
||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
edges: [{ node: newData, cursor: '' }],
|
||||
pageInfo: {
|
||||
endCursor: '',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
}): OptimisticEffectDefinition => ({
|
||||
typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
resolver: ({
|
||||
currentCacheData: currentData,
|
||||
createdRecords,
|
||||
updatedRecords,
|
||||
deletedRecordIds,
|
||||
variables,
|
||||
}) => {
|
||||
const newRecordPaginatedCacheField = produce<
|
||||
PaginatedRecordTypeResults<any>
|
||||
>(currentData as PaginatedRecordTypeResults<any>, (draft) => {
|
||||
const existingDataIsEmpty = !draft || !draft.edges || !draft.edges[0];
|
||||
|
||||
const existingRecord = draft.edges.find(
|
||||
(edge) => edge.node.id === newData.id,
|
||||
);
|
||||
if (existingRecord) {
|
||||
existingRecord.node = newData;
|
||||
return;
|
||||
}
|
||||
|
||||
draft.edges.unshift({
|
||||
node: newData,
|
||||
cursor: '',
|
||||
if (isNonEmptyArray(createdRecords)) {
|
||||
if (existingDataIsEmpty) {
|
||||
return {
|
||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
});
|
||||
}
|
||||
edges: createdRecords.map((createdRecord) => ({
|
||||
node: createdRecord,
|
||||
cursor: '',
|
||||
})),
|
||||
pageInfo: {
|
||||
endCursor: '',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
},
|
||||
};
|
||||
} else {
|
||||
for (const createdRecord of createdRecords) {
|
||||
const existingRecord = draft.edges.find(
|
||||
(edge) => edge.node.id === createdRecord.id,
|
||||
);
|
||||
|
||||
if (deletedRecordIds) {
|
||||
draft.edges = draft.edges.filter(
|
||||
(edge) => !deletedRecordIds.includes(edge.node.id),
|
||||
);
|
||||
}
|
||||
});
|
||||
if (existingRecord) {
|
||||
existingRecord.node = createdRecord;
|
||||
continue;
|
||||
}
|
||||
|
||||
return newRecordPaginatedCacheField;
|
||||
},
|
||||
isUsingFlexibleBackend: true,
|
||||
objectMetadataItem,
|
||||
}) satisfies OptimisticEffectDefinition;
|
||||
draft.edges.unshift({
|
||||
node: createdRecord,
|
||||
cursor: '',
|
||||
__typename: `${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deletedRecordIds) {
|
||||
draft.edges = draft.edges.filter(
|
||||
(edge) => !deletedRecordIds.includes(edge.node.id),
|
||||
);
|
||||
}
|
||||
|
||||
if (isNonEmptyArray(updatedRecords)) {
|
||||
for (const updatedRecord of updatedRecords) {
|
||||
const updatedRecordIsOutOfQueryFilter =
|
||||
isDefined(variables.filter) &&
|
||||
!isRecordMatchingFilter({
|
||||
record: updatedRecord,
|
||||
filter: variables.filter,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
if (updatedRecordIsOutOfQueryFilter) {
|
||||
draft.edges = draft.edges.filter(
|
||||
(edge) => edge.node.id !== updatedRecord.id,
|
||||
);
|
||||
} else {
|
||||
const foundUpdatedRecordInCacheQuery = draft.edges.find(
|
||||
(edge) => edge.node.id === updatedRecord.id,
|
||||
);
|
||||
|
||||
if (foundUpdatedRecordInCacheQuery) {
|
||||
foundUpdatedRecordInCacheQuery.node = updatedRecord;
|
||||
} else {
|
||||
// TODO: add order by
|
||||
draft.edges.push({
|
||||
node: updatedRecord,
|
||||
cursor: '',
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Edge`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return newRecordPaginatedCacheField;
|
||||
},
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user