Activity cache injection (#3791)

* WIP

* Minor fixes

* Added TODO

* Fix post merge

* Fix

* Fixed warnings

* Fixed comments

* Fixed comments

* Fixed naming

* Removed comment

* WIP

* WIP 2

* Finished working version

* Fixes

* Fixed typing

* Fixes

* Fixes

* Fixes

* Naming fixes

* WIP

* Fix import

* WIP

* Working version on title

* Fixed create record id overwrite

* Removed unecessary callback

* Masterpiece

* Fixed delete on click outside drawer or delete

* Cleaned

* Cleaned

* Cleaned

* Minor fixes

* Fixes

* Fixed naming

* WIP

* Fix

* Fixed create from target inline cell

* Removed console.log

* Fixed delete activity optimistic effect

* Fixed no title

* Fixed debounce and title body creation

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2024-02-09 14:51:30 +01:00
committed by GitHub
parent 9ceff84bbf
commit cca72da708
87 changed files with 2195 additions and 1058 deletions

View File

@ -2,14 +2,18 @@ import { useApolloClient } from '@apollo/client';
import { v4 } from 'uuid';
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { useGenerateObjectRecordOptimisticResponse } from '@/object-record/cache/hooks/useGenerateObjectRecordOptimisticResponse';
import { getCreateManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
type CreateManyRecordsOptions = {
skipOptimisticEffect?: boolean;
};
export const useCreateManyRecords = <
CreatedObjectRecord extends ObjectRecord = ObjectRecord,
>({
@ -27,15 +31,22 @@ export const useCreateManyRecords = <
objectMetadataItem,
});
const getRelationMetadata = useGetRelationMetadata();
const { objectMetadataItems } = useObjectMetadataItems();
const createManyRecords = async (data: Partial<CreatedObjectRecord>[]) => {
const sanitizedCreateManyRecordsInput = data.map((input) =>
sanitizeRecordInput({
const createManyRecords = async (
data: Partial<CreatedObjectRecord>[],
options?: CreateManyRecordsOptions,
) => {
const sanitizedCreateManyRecordsInput = data.map((input) => {
const idForCreation = input.id ?? v4();
const sanitizedRecordInput = sanitizeRecordInput({
objectMetadataItem,
recordInput: { ...input, id: v4() },
}),
);
recordInput: { ...input, id: idForCreation },
});
return sanitizedRecordInput;
});
const optimisticallyCreatedRecords = sanitizedCreateManyRecordsInput.map(
(record) =>
@ -51,21 +62,25 @@ export const useCreateManyRecords = <
variables: {
data: sanitizedCreateManyRecordsInput,
},
optimisticResponse: {
[mutationResponseField]: optimisticallyCreatedRecords,
},
update: (cache, { data }) => {
const records = data?.[mutationResponseField];
optimisticResponse: options?.skipOptimisticEffect
? undefined
: {
[mutationResponseField]: optimisticallyCreatedRecords,
},
update: options?.skipOptimisticEffect
? undefined
: (cache, { data }) => {
const records = data?.[mutationResponseField];
if (!records?.length) return;
if (!records?.length) return;
triggerCreateRecordsOptimisticEffect({
cache,
objectMetadataItem,
records,
getRelationMetadata,
});
},
triggerCreateRecordsOptimisticEffect({
cache,
objectMetadataItem,
recordsToCreate: records,
objectMetadataItems,
});
},
});
return createdObjects.data?.[mutationResponseField] ?? [];

View File

@ -22,7 +22,7 @@ export const useCreateManyRecordsInCache = <T extends ObjectRecord>({
objectMetadataItem,
});
const createManyRecordsInCache = async (data: Partial<T>[]) => {
const createManyRecordsInCache = (data: Partial<T>[]) => {
const recordsWithId = data.map((record) => ({
...record,
id: (record.id as string) ?? v4(),

View File

@ -2,8 +2,8 @@ import { useApolloClient } from '@apollo/client';
import { v4 } from 'uuid';
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useGenerateObjectRecordOptimisticResponse } from '@/object-record/cache/hooks/useGenerateObjectRecordOptimisticResponse';
import { getCreateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
@ -13,6 +13,10 @@ type useCreateOneRecordProps = {
objectNameSingular: string;
};
type CreateOneRecordOptions = {
skipOptimisticEffect?: boolean;
};
export const useCreateOneRecord = <
CreatedObjectRecord extends ObjectRecord = ObjectRecord,
>({
@ -29,12 +33,17 @@ export const useCreateOneRecord = <
objectMetadataItem,
});
const getRelationMetadata = useGetRelationMetadata();
const { objectMetadataItems } = useObjectMetadataItems();
const createOneRecord = async (
input: Partial<CreatedObjectRecord>,
options?: CreateOneRecordOptions,
) => {
const idForCreation = input.id ?? v4();
const createOneRecord = async (input: Partial<CreatedObjectRecord>) => {
const sanitizedCreateOneRecordInput = sanitizeRecordInput({
objectMetadataItem,
recordInput: { ...input, id: v4() },
recordInput: { ...input, id: idForCreation },
});
const optimisticallyCreatedRecord =
@ -51,21 +60,25 @@ export const useCreateOneRecord = <
variables: {
input: sanitizedCreateOneRecordInput,
},
optimisticResponse: {
[mutationResponseField]: optimisticallyCreatedRecord,
},
update: (cache, { data }) => {
const record = data?.[mutationResponseField];
optimisticResponse: options?.skipOptimisticEffect
? undefined
: {
[mutationResponseField]: optimisticallyCreatedRecord,
},
update: options?.skipOptimisticEffect
? undefined
: (cache, { data }) => {
const record = data?.[mutationResponseField];
if (!record) return;
if (!record) return;
triggerCreateRecordsOptimisticEffect({
cache,
objectMetadataItem,
records: [record],
getRelationMetadata,
});
},
triggerCreateRecordsOptimisticEffect({
cache,
objectMetadataItem,
recordsToCreate: [record],
objectMetadataItems,
});
},
});
return createdObject.data?.[mutationResponseField] ?? null;

View File

@ -23,7 +23,7 @@ export const useCreateOneRecordInCache = <T>({
objectMetadataItem,
});
const createOneRecordInCache = async (input: ObjectRecord) => {
const createOneRecordInCache = (input: ObjectRecord) => {
const generatedCachedObjectRecord =
generateObjectRecordOptimisticResponse(input);

View File

@ -1,8 +1,8 @@
import { useApolloClient } from '@apollo/client';
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
import { isDefined } from '~/utils/isDefined';
import { capitalize } from '~/utils/string/capitalize';
@ -20,7 +20,7 @@ export const useDeleteManyRecords = ({
const { objectMetadataItem, deleteManyRecordsMutation, getRecordFromCache } =
useObjectMetadataItem({ objectNameSingular });
const getRelationMetadata = useGetRelationMetadata();
const { objectMetadataItems } = useObjectMetadataItems();
const mutationResponseField = getDeleteManyRecordsMutationResponseField(
objectMetadataItem.namePlural,
@ -50,8 +50,8 @@ export const useDeleteManyRecords = ({
triggerDeleteRecordsOptimisticEffect({
cache,
objectMetadataItem,
records: cachedRecords,
getRelationMetadata,
recordsToDelete: cachedRecords,
objectMetadataItems,
});
},
});

View File

@ -2,8 +2,8 @@ import { useCallback } from 'react';
import { useApolloClient } from '@apollo/client';
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/generateDeleteOneRecordMutation';
import { capitalize } from '~/utils/string/capitalize';
@ -20,7 +20,7 @@ export const useDeleteOneRecord = ({
const { objectMetadataItem, deleteOneRecordMutation, getRecordFromCache } =
useObjectMetadataItem({ objectNameSingular });
const getRelationMetadata = useGetRelationMetadata();
const { objectMetadataItems } = useObjectMetadataItems();
const mutationResponseField =
getDeleteOneRecordMutationResponseField(objectNameSingular);
@ -48,8 +48,8 @@ export const useDeleteOneRecord = ({
triggerDeleteRecordsOptimisticEffect({
cache,
objectMetadataItem,
records: [cachedRecord],
getRelationMetadata,
recordsToDelete: [cachedRecord],
objectMetadataItems,
});
},
});
@ -60,10 +60,10 @@ export const useDeleteOneRecord = ({
apolloClient,
deleteOneRecordMutation,
getRecordFromCache,
getRelationMetadata,
mutationResponseField,
objectMetadataItem,
objectNameSingular,
objectMetadataItems,
],
);

View File

@ -17,6 +17,7 @@ export const useFieldContext = ({
isLabelIdentifier = false,
objectNameSingular,
objectRecordId,
customUseUpdateOneObjectHook,
}: {
clearable?: boolean;
fieldMetadataName: string;
@ -24,6 +25,7 @@ export const useFieldContext = ({
isLabelIdentifier?: boolean;
objectNameSingular: string;
objectRecordId: string;
customUseUpdateOneObjectHook?: RecordUpdateHook;
}) => {
const { basePathToShowPage, objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
@ -65,7 +67,8 @@ export const useFieldContext = ({
position: fieldPosition,
objectMetadataItem,
}),
useUpdateRecord: useUpdateOneObjectMutation,
useUpdateRecord:
customUseUpdateOneObjectHook ?? useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
clearable,
}}

View File

@ -2,10 +2,9 @@ import { useQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export const useFindOneRecord = <
ObjectType extends { id: string } & Record<string, any>,
>({
export const useFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
objectRecordId = '',
onCompleted,
@ -13,7 +12,7 @@ export const useFindOneRecord = <
skip,
}: ObjectMetadataItemIdentifier & {
objectRecordId: string | undefined;
onCompleted?: (data: ObjectType) => void;
onCompleted?: (data: T) => void;
skip?: boolean;
depth?: number;
}) => {
@ -23,7 +22,7 @@ export const useFindOneRecord = <
);
const { data, loading, error } = useQuery<
{ [nameSingular: string]: ObjectType },
{ [nameSingular: string]: T },
{ objectRecordId: string }
>(findOneRecordQuery, {
skip: !objectMetadataItem || !objectRecordId || skip,

View File

@ -31,7 +31,11 @@ export const useGenerateCreateManyRecordMutation = ({
${mutationResponseField}(data: $data) {
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
}),
)
.join('\n')}
}
}`;

View File

@ -31,7 +31,11 @@ export const useGenerateCreateOneRecordMutation = ({
${mutationResponseField}(data: $input) {
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
}),
)
.join('\n')}
}
}

View File

@ -36,7 +36,11 @@ export const useGenerateExecuteQuickActionOnOneRecordMutation = ({
${graphQLFieldForExecuteQuickActionOnOneRecordMutation}(id: $idToExecuteQuickActionOn) {
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
}),
)
.join('\n')}
}
}

View File

@ -71,7 +71,12 @@ export const useGenerateFindManyRecordsForMultipleMetadataItemsQuery = ({
node {
id
${fields
.map((field) => mapFieldMetadataToGraphQLQuery(field, depth))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
maxDepthForRelations: depth,
}),
)
.join('\n')}
}
cursor

View File

@ -28,7 +28,12 @@ export const useGenerateFindManyRecordsQuery = () => {
node {
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field, depth))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
maxDepthForRelations: depth,
}),
)
.join('\n')}
}
cursor

View File

@ -12,8 +12,8 @@ export const useGenerateFindOneRecordQuery = () => {
}: {
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
depth?: number;
}) =>
gql`
}) => {
return gql`
query FindOne${objectMetadataItem.nameSingular}($objectRecordId: UUID!) {
${objectMetadataItem.nameSingular}(filter: {
id: {
@ -22,9 +22,15 @@ export const useGenerateFindOneRecordQuery = () => {
}){
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field, depth))
.map((field) =>
mapFieldMetadataToGraphQLQuery({
field,
maxDepthForRelations: depth,
}),
)
.join('\n')}
}
}
`;
`;
};
};

View File

@ -31,7 +31,7 @@ export const useGenerateUpdateOneRecordMutation = ({
${mutationResponseField}(id: $idToUpdate, data: $input) {
id
${objectMetadataItem.fields
.map((field) => mapFieldMetadataToGraphQLQuery(field))
.map((field) => mapFieldMetadataToGraphQLQuery({ field }))
.join('\n')}
}
}

View File

@ -1,8 +1,8 @@
import { useApolloClient } from '@apollo/client';
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useGenerateObjectRecordOptimisticResponse } from '@/object-record/cache/hooks/useGenerateObjectRecordOptimisticResponse';
import { getUpdateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateUpdateOneRecordMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
@ -27,7 +27,7 @@ export const useUpdateOneRecord = <
objectMetadataItem,
});
const getRelationMetadata = useGetRelationMetadata();
const { objectMetadataItems } = useObjectMetadataItems();
const updateOneRecord = async ({
idToUpdate,
@ -69,9 +69,9 @@ export const useUpdateOneRecord = <
triggerUpdateRecordOptimisticEffect({
cache,
objectMetadataItem,
previousRecord: cachedRecord,
nextRecord: record,
getRelationMetadata,
currentRecord: cachedRecord,
updatedRecord: record,
objectMetadataItems,
});
},
});