Skip to content

Commit

Permalink
Fixed various bugs in activity creation (#6208)
Browse files Browse the repository at this point in the history
- Fixed activity creation in cache
- Fixed activity creation in DB, where the relation target was
disappearing after creation
- Added an option to match root query filter in creation optimistic
effect to avoid adding the newly created record in every mounted query
in Apollo cache on the same object (which was causing notes to be
duplicated on every object in the cache)
- Fixed tab list scope id
- Fixed various browser console warnings
  • Loading branch information
lucasbordeau authored Jul 10, 2024
1 parent 34d13a7 commit 6bc3663
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,98 @@ import { isNonEmptyArray } from '@sniptt/guards';
import { CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE } from '@/activities/graphql/operation-signatures/CreateOneActivityOperationSignature';
import { ActivityForEditor } from '@/activities/types/ActivityForEditor';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useApolloClient } from '@apollo/client';

import { useRecoilCallback } from 'recoil';
import { capitalize } from '~/utils/string/capitalize';

export const useCreateActivityInDB = () => {
const { createOneRecord: createOneActivity } = useCreateOneRecord({
objectNameSingular:
CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.objectNameSingular,
recordGqlFields: CREATE_ONE_ACTIVITY_OPERATION_SIGNATURE.fields,
shouldMatchRootQueryFilter: true,
});

const { createManyRecords: createManyActivityTargets } =
useCreateManyRecords<ActivityTarget>({
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
skipPostOptmisticEffect: true,
shouldMatchRootQueryFilter: true,
});

const { objectMetadataItems } = useObjectMetadataItems();

const { objectMetadataItem: objectMetadataItemActivityTarget } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
});

const createActivityInDB = async (activityToCreate: ActivityForEditor) => {
await createOneActivity?.({
...activityToCreate,
updatedAt: new Date().toISOString(),
const { objectMetadataItem: objectMetadataItemActivity } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.Activity,
});

const activityTargetsToCreate = activityToCreate.activityTargets ?? [];
const cache = useApolloClient().cache;

if (isNonEmptyArray(activityTargetsToCreate)) {
await createManyActivityTargets(activityTargetsToCreate);
}
};
const createActivityInDB = useRecoilCallback(
({ set }) =>
async (activityToCreate: ActivityForEditor) => {
const createdActivity = await createOneActivity?.({
...activityToCreate,
updatedAt: new Date().toISOString(),
});

const activityTargetsToCreate = activityToCreate.activityTargets ?? [];

if (isNonEmptyArray(activityTargetsToCreate)) {
await createManyActivityTargets(activityTargetsToCreate);
}

const activityTargetsConnection = getRecordConnectionFromRecords({
objectMetadataItems,
objectMetadataItem: objectMetadataItemActivityTarget,
records: activityTargetsToCreate.map((activityTarget) => ({
...activityTarget,
__typename: capitalize(
objectMetadataItemActivityTarget.nameSingular,
),
})),
withPageInfo: false,
computeReferences: true,
isRootLevel: false,
});

modifyRecordFromCache({
recordId: createdActivity.id,
cache,
fieldModifiers: {
activityTargets: () => activityTargetsConnection,
},
objectMetadataItem: objectMetadataItemActivity,
});

set(recordStoreFamilyState(createdActivity.id), {
...createdActivity,
activityTargets: activityTargetsToCreate,
});
},
[
cache,
createManyActivityTargets,
createOneActivity,
objectMetadataItemActivity,
objectMetadataItemActivityTarget,
objectMetadataItems,
],
);

return {
createActivityInDB,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,44 +1,30 @@
import { useSetRecoilState } from 'recoil';
import { IconCheckbox, IconNotes, IconPaperclip } from 'twenty-ui';

import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { Button } from '@/ui/input/button/components/Button';
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
import { TAB_LIST_COMPONENT_ID } from '@/ui/layout/show-page/components/ShowPageRightContainer';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';

export const TimelineCreateButtonGroup = ({
targetableObject,
}: {
targetableObject: ActivityTargetableObject;
}) => {
export const TimelineCreateButtonGroup = () => {
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
const setActiveTabId = useSetRecoilState(activeTabIdState);

const openCreateActivity = useOpenCreateActivityDrawer();

return (
<ButtonGroup variant={'secondary'}>
<Button
Icon={IconNotes}
title="Note"
onClick={() =>
openCreateActivity({
type: 'Note',
targetableObjects: [targetableObject],
})
}
onClick={() => {
setActiveTabId('notes');
}}
/>
<Button
Icon={IconCheckbox}
title="Task"
onClick={() =>
openCreateActivity({
type: 'Task',
targetableObjects: [targetableObject],
})
}
onClick={() => {
setActiveTabId('tasks');
}}
/>
<Button
Icon={IconPaperclip}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const TimelineActivities = ({
There are no activities associated with this record.{' '}
</AnimatedPlaceholderEmptySubTitle>
</AnimatedPlaceholderEmptyTextContainer>
<TimelineCreateButtonGroup targetableObject={targetableObject} />
<TimelineCreateButtonGroup />
</AnimatedPlaceholderEmptyContainer>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { isRecordMatchingFilter } from '@/object-record/record-filter/utils/isRecordMatchingFilter';

import { CachedObjectRecordQueryVariables } from '@/apollo/types/CachedObjectRecordQueryVariables';
import { isDefined } from '~/utils/isDefined';
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';

/*
TODO: for now new records are added to all cached record lists, no matter what the variables (filters, orderBy, etc.) are.
Expand All @@ -19,11 +23,13 @@ export const triggerCreateRecordsOptimisticEffect = ({
objectMetadataItem,
recordsToCreate,
objectMetadataItems,
shouldMatchRootQueryFilter,
}: {
cache: ApolloCache<unknown>;
objectMetadataItem: ObjectMetadataItem;
recordsToCreate: RecordGqlNode[];
objectMetadataItems: ObjectMetadataItem[];
shouldMatchRootQueryFilter?: boolean;
}) => {
recordsToCreate.forEach((record) =>
triggerUpdateRelationsOptimisticEffect({
Expand All @@ -39,12 +45,7 @@ export const triggerCreateRecordsOptimisticEffect = ({
fields: {
[objectMetadataItem.namePlural]: (
rootQueryCachedResponse,
{
DELETE: _DELETE,
readField,
storeFieldName: _storeFieldName,
toReference,
},
{ DELETE: _DELETE, readField, storeFieldName, toReference },
) => {
const shouldSkip = !isObjectRecordConnectionWithRefs(
objectMetadataItem.nameSingular,
Expand All @@ -55,6 +56,13 @@ export const triggerCreateRecordsOptimisticEffect = ({
return rootQueryCachedResponse;
}

const { fieldVariables: rootQueryVariables } =
parseApolloStoreFieldName<CachedObjectRecordQueryVariables>(
storeFieldName,
);

const rootQueryFilter = rootQueryVariables?.filter;

const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;

const rootQueryCachedRecordEdges = readField<RecordGqlRefEdge[]>(
Expand All @@ -74,6 +82,22 @@ export const triggerCreateRecordsOptimisticEffect = ({
const hasAddedRecords = recordsToCreate
.map((recordToCreate) => {
if (isNonEmptyString(recordToCreate.id)) {
if (
isDefined(rootQueryFilter) &&
shouldMatchRootQueryFilter === true
) {
const recordToCreateMatchesThisRootQueryFilter =
isRecordMatchingFilter({
record: recordToCreate,
filter: rootQueryFilter,
objectMetadataItem,
});

if (!recordToCreateMatchesThisRootQueryFilter) {
return false;
}
}

const recordToCreateReference = toReference(recordToCreate);

if (!recordToCreateReference) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type useCreateManyRecordsProps = {
objectNameSingular: string;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
skipPostOptmisticEffect?: boolean;
shouldMatchRootQueryFilter?: boolean;
};

export const useCreateManyRecords = <
Expand All @@ -26,6 +27,7 @@ export const useCreateManyRecords = <
objectNameSingular,
recordGqlFields,
skipPostOptmisticEffect = false,
shouldMatchRootQueryFilter,
}: useCreateManyRecordsProps) => {
const apolloClient = useApolloClient();

Expand Down Expand Up @@ -88,6 +90,7 @@ export const useCreateManyRecords = <
objectMetadataItem,
recordsToCreate: recordsCreatedInCache,
objectMetadataItems,
shouldMatchRootQueryFilter,
});
}

Expand All @@ -111,6 +114,7 @@ export const useCreateManyRecords = <
objectMetadataItem,
recordsToCreate: records,
objectMetadataItems,
shouldMatchRootQueryFilter,
});
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useApolloClient } from '@apollo/client';
import { useState } from 'react';
import { v4 } from 'uuid';

import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
Expand All @@ -19,6 +19,7 @@ type useCreateOneRecordProps = {
objectNameSingular: string;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
skipPostOptmisticEffect?: boolean;
shouldMatchRootQueryFilter?: boolean;
};

export const useCreateOneRecord = <
Expand All @@ -27,6 +28,7 @@ export const useCreateOneRecord = <
objectNameSingular,
recordGqlFields,
skipPostOptmisticEffect = false,
shouldMatchRootQueryFilter,
}: useCreateOneRecordProps) => {
const apolloClient = useApolloClient();
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -76,6 +78,7 @@ export const useCreateOneRecord = <
objectMetadataItem,
recordsToCreate: [recordCreatedInCache],
objectMetadataItems,
shouldMatchRootQueryFilter,
});
}

Expand All @@ -97,7 +100,9 @@ export const useCreateOneRecord = <
objectMetadataItem,
recordsToCreate: [record],
objectMetadataItems,
shouldMatchRootQueryFilter,
});

setLoading(false);
},
});
Expand Down
Loading

0 comments on commit 6bc3663

Please sign in to comment.