Refactor MultipleObjectsPicker component (#10552)

Refactor to only have MultipleRecordPicker and SingleRecordPicker

What's done:
- SingleRecordPicker, MultipleRecordPicker
- RelationToOneInput
- RelationFromManyInput
- usage in TableCell, InlineCell, RelationDetailSection, Workflow

What's left:
- Make a pass on the app, to make sure the hotkeyScopes, clickOutside
are properly set
- Fix flashing on ActivityTarget
- add more tests on the code
This commit is contained in:
Charles Bochet
2025-03-10 15:04:09 +01:00
committed by GitHub
parent 7eabcc8774
commit f0de6d31b7
126 changed files with 2465 additions and 2242 deletions

View File

@ -1,95 +0,0 @@
import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/activities/inline-cell/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const instanceId = 'instanceId';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecordPickerComponentInstanceContext.Provider value={{ instanceId }}>
<RecoilRoot>{children}</RecoilRoot>
</RecordPickerComponentInstanceContext.Provider>
);
const opportunityId = 'cb702502-4b1d-488e-9461-df3fb096ebf6';
const personId = 'ab091fd9-1b81-4dfd-bfdb-564ffee032a2';
describe('useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray', () => {
it('should return object formatted from objectMetadataItemsState', async () => {
const { result } = renderHook(
() => {
return {
formattedRecord:
useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray(
{
multiObjectRecordsQueryResult: {
opportunities: {
edges: [
{
node: {
id: opportunityId,
pointOfContactId:
'e992bda7-d797-4e12-af04-9b427f42244c',
updatedAt: '2023-11-30T11:13:15.308Z',
createdAt: '2023-11-30T11:13:15.308Z',
__typename: 'Opportunity',
},
cursor: 'cursor',
__typename: 'OpportunityEdge',
},
],
pageInfo: {},
},
people: {
edges: [
{
node: {
id: personId,
updatedAt: '2023-11-30T11:13:15.308Z',
createdAt: '2023-11-30T11:13:15.308Z',
__typename: 'Person',
},
cursor: 'cursor',
__typename: 'PersonEdge',
},
],
pageInfo: {},
},
},
},
),
setObjectMetadata: useSetRecoilState(objectMetadataItemsState),
};
},
{
wrapper: Wrapper,
},
);
act(() => {
result.current.setObjectMetadata(generatedMockObjectMetadataItems);
});
expect(
result.current.formattedRecord.objectRecordForSelectArray.length,
).toBe(2);
const [opportunityRecordForSelect, personRecordForSelect] =
result.current.formattedRecord.objectRecordForSelectArray;
expect(opportunityRecordForSelect.objectMetadataItem.namePlural).toBe(
'opportunities',
);
expect(opportunityRecordForSelect.record.id).toBe(opportunityId);
expect(opportunityRecordForSelect.recordIdentifier.linkToShowPage).toBe(
`/object/opportunity/${opportunityId}`,
);
expect(personRecordForSelect.objectMetadataItem.namePlural).toBe('people');
expect(personRecordForSelect.record.id).toBe(personId);
expect(personRecordForSelect.recordIdentifier.linkToShowPage).toBe(
`/object/person/${personId}`,
);
});
});

View File

@ -1,97 +0,0 @@
import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/activities/inline-cell/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordPickerComponentInstanceContext } from '@/object-record/record-picker/states/contexts/RecordPickerComponentInstanceContext';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
const instanceId = 'instanceId';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecordPickerComponentInstanceContext.Provider value={{ instanceId }}>
<RecoilRoot>{children}</RecoilRoot>
</RecordPickerComponentInstanceContext.Provider>
);
const opportunityId = 'cb702502-4b1d-488e-9461-df3fb096ebf6';
const personId = 'ab091fd9-1b81-4dfd-bfdb-564ffee032a2';
describe('useMultiObjectRecordsQueryResultFormattedAsObjectRecordsMap', () => {
it('should return object formatted from objectMetadataItemsState', async () => {
const { result } = renderHook(
() => {
return {
formattedRecord:
useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap({
multiObjectRecordsQueryResult: {
opportunities: {
edges: [
{
node: {
id: opportunityId,
pointOfContactId:
'e992bda7-d797-4e12-af04-9b427f42244c',
updatedAt: '2023-11-30T11:13:15.308Z',
createdAt: '2023-11-30T11:13:15.308Z',
__typename: 'Opportunity',
},
cursor: 'cursor',
__typename: 'OpportunityEdge',
},
],
pageInfo: {},
},
people: {
edges: [
{
node: {
id: personId,
updatedAt: '2023-11-30T11:13:15.308Z',
createdAt: '2023-11-30T11:13:15.308Z',
__typename: 'Person',
},
cursor: 'cursor',
__typename: 'PersonEdge',
},
],
pageInfo: {},
},
},
}),
setObjectMetadata: useSetRecoilState(objectMetadataItemsState),
};
},
{
wrapper: Wrapper,
},
);
act(() => {
result.current.setObjectMetadata(generatedMockObjectMetadataItems);
});
expect(
Object.values(result.current.formattedRecord.objectRecordsMap).flat()
.length,
).toBe(2);
const opportunityObjectRecords =
result.current.formattedRecord.objectRecordsMap.opportunities;
const personObjectRecords =
result.current.formattedRecord.objectRecordsMap.people;
expect(opportunityObjectRecords[0].objectMetadataItem.namePlural).toBe(
'opportunities',
);
expect(opportunityObjectRecords[0].record.id).toBe(opportunityId);
expect(opportunityObjectRecords[0].recordIdentifier.linkToShowPage).toBe(
`/object/opportunity/${opportunityId}`,
);
expect(personObjectRecords[0].objectMetadataItem.namePlural).toBe('people');
expect(personObjectRecords[0].record.id).toBe(personId);
expect(personObjectRecords[0].recordIdentifier.linkToShowPage).toBe(
`/object/person/${personId}`,
);
});
});

View File

@ -1,55 +0,0 @@
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { MultiObjectRecordQueryResult } from '@/object-record/multiple-objects/types/MultiObjectRecordQueryResult';
import { formatMultiObjectRecordSearchResults } from '@/object-record/multiple-objects/utils/formatMultiObjectRecordSearchResults';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { isDefined } from 'twenty-shared';
export const useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray =
({
multiObjectRecordsQueryResult,
}: {
multiObjectRecordsQueryResult:
| MultiObjectRecordQueryResult
| null
| undefined;
}) => {
const objectMetadataItemsByNamePluralMap = useRecoilValue(
objectMetadataItemsByNamePluralMapSelector,
);
const formattedMultiObjectRecordsQueryResult = useMemo(() => {
return formatMultiObjectRecordSearchResults(
multiObjectRecordsQueryResult,
);
}, [multiObjectRecordsQueryResult]);
const objectRecordForSelectArray = useMemo(() => {
return Object.entries(
formattedMultiObjectRecordsQueryResult ?? {},
).flatMap(([namePlural, objectRecordConnection]) => {
const objectMetadataItem =
objectMetadataItemsByNamePluralMap.get(namePlural);
if (!isDefined(objectMetadataItem)) return [];
return objectRecordConnection.edges.map(({ node }) => ({
objectMetadataItem,
record: node,
recordIdentifier: getObjectRecordIdentifier({
objectMetadataItem,
record: node,
}),
})) as ObjectRecordForSelect[];
});
}, [
formattedMultiObjectRecordsQueryResult,
objectMetadataItemsByNamePluralMap,
]);
return {
objectRecordForSelectArray,
};
};

View File

@ -1,62 +0,0 @@
import { useQuery } from '@apollo/client';
import { useRecoilValue } from 'recoil';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { useGenerateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedSearchRecordsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/multiple-objects/types/MultiObjectRecordQueryResult';
import { isDefined } from 'twenty-shared';
export const useMultiObjectSearch = ({
searchFilterValue,
limit,
excludedObjects,
}: {
searchFilterValue: string;
limit?: number;
excludedObjects?: CoreObjectNameSingular[];
}) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const selectableObjectMetadataItems = objectMetadataItems.filter(
({ nameSingular, isSearchable }) =>
!excludedObjects?.includes(nameSingular as CoreObjectNameSingular) &&
isSearchable,
);
const { limitPerMetadataItem } = useLimitPerMetadataItem({
objectMetadataItems,
limit,
});
const multiSelectSearchQueryForSelectedIds =
useGenerateCombinedSearchRecordsQuery({
operationSignatures: selectableObjectMetadataItems.map(
(objectMetadataItem) => ({
objectNameSingular: objectMetadataItem.nameSingular,
variables: {},
}),
),
});
const {
loading: matchesSearchFilterObjectRecordsLoading,
data: matchesSearchFilterObjectRecordsQueryResult,
} = useQuery<MultiObjectRecordQueryResult>(
multiSelectSearchQueryForSelectedIds ?? EMPTY_QUERY,
{
variables: {
search: searchFilterValue,
...limitPerMetadataItem,
},
skip: !isDefined(multiSelectSearchQueryForSelectedIds),
},
);
return {
matchesSearchFilterObjectRecordsLoading,
matchesSearchFilterObjectRecordsQueryResult,
};
};

View File

@ -1,61 +0,0 @@
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { MultiObjectRecordQueryResult } from '@/object-record/multiple-objects/types/MultiObjectRecordQueryResult';
import { formatMultiObjectRecordSearchResults } from '@/object-record/multiple-objects/utils/formatMultiObjectRecordSearchResults';
import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect';
import { isDefined } from 'twenty-shared';
export const useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap = ({
multiObjectRecordsQueryResult,
}: {
multiObjectRecordsQueryResult:
| MultiObjectRecordQueryResult
| null
| undefined;
}) => {
const objectMetadataItemsByNamePluralMap = useRecoilValue(
objectMetadataItemsByNamePluralMapSelector,
);
const formattedMultiObjectRecordsQueryResult = useMemo(() => {
return formatMultiObjectRecordSearchResults(multiObjectRecordsQueryResult);
}, [multiObjectRecordsQueryResult]);
const objectRecordsMap = useMemo(() => {
const recordsByNamePlural: { [key: string]: ObjectRecordForSelect[] } = {};
Object.entries(formattedMultiObjectRecordsQueryResult ?? {}).forEach(
([namePlural, objectRecordConnection]) => {
const objectMetadataItem =
objectMetadataItemsByNamePluralMap.get(namePlural);
if (!isDefined(objectMetadataItem)) return [];
if (!isDefined(recordsByNamePlural[namePlural])) {
recordsByNamePlural[namePlural] = [];
}
objectRecordConnection.edges.forEach(({ node }) => {
const record = {
objectMetadataItem,
record: node,
recordIdentifier: getObjectRecordIdentifier({
objectMetadataItem,
record: node,
}),
} as ObjectRecordForSelect;
recordsByNamePlural[namePlural].push(record);
});
},
);
return recordsByNamePlural;
}, [
formattedMultiObjectRecordsQueryResult,
objectMetadataItemsByNamePluralMap,
]);
return {
objectRecordsMap,
};
};

View File

@ -1,100 +0,0 @@
import { gql, useQuery } from '@apollo/client';
import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/activities/inline-cell/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
import { useLimitPerMetadataItem } from '@/object-metadata/hooks/useLimitPerMetadataItem';
import { useOrderByFieldPerMetadataItem } from '@/object-metadata/hooks/useOrderByFieldPerMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/multiple-objects/types/MultiObjectRecordQueryResult';
import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId';
import { capitalize, isDefined } from 'twenty-shared';
export const EMPTY_QUERY = gql`
query Empty {
__typename
}
`;
export const useMultiObjectSearchSelectedItemsQuery = ({
selectedObjectRecordIds,
}: {
selectedObjectRecordIds: SelectedObjectRecordId[];
}) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItemsUsedInSelectedIdsQuery = objectMetadataItems.filter(
({ nameSingular }) => {
return selectedObjectRecordIds.some(({ objectNameSingular }) => {
return objectNameSingular === nameSingular;
});
},
);
const selectedIdFilterPerMetadataItem = Object.fromEntries(
objectMetadataItemsUsedInSelectedIdsQuery
.map(({ nameSingular }) => {
const selectedIds = selectedObjectRecordIds
.filter(
({ objectNameSingular }) => objectNameSingular === nameSingular,
)
.map(({ id }) => id);
if (!isNonEmptyArray(selectedIds)) return null;
return [
`filter${capitalize(nameSingular)}`,
{
id: {
in: selectedIds,
},
},
];
})
.filter(isDefined),
);
const { orderByFieldPerMetadataItem } = useOrderByFieldPerMetadataItem({
objectMetadataItems: objectMetadataItemsUsedInSelectedIdsQuery,
});
const { limitPerMetadataItem } = useLimitPerMetadataItem({
objectMetadataItems: objectMetadataItemsUsedInSelectedIdsQuery,
});
const multiSelectQueryForSelectedIds =
useGenerateCombinedFindManyRecordsQuery({
operationSignatures: objectMetadataItemsUsedInSelectedIdsQuery.map(
(objectMetadataItem) => ({
objectNameSingular: objectMetadataItem.nameSingular,
variables: {},
}),
),
});
const {
loading: selectedObjectRecordsLoading,
data: selectedObjectRecordsQueryResult,
} = useQuery<MultiObjectRecordQueryResult>(
multiSelectQueryForSelectedIds ?? EMPTY_QUERY,
{
variables: {
...selectedIdFilterPerMetadataItem,
...orderByFieldPerMetadataItem,
...limitPerMetadataItem,
},
skip: !isDefined(multiSelectQueryForSelectedIds),
},
);
const { objectRecordForSelectArray: selectedObjectRecords } =
useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray({
multiObjectRecordsQueryResult: selectedObjectRecordsQueryResult,
});
return {
selectedObjectRecordsLoading,
selectedObjectRecords,
};
};

View File

@ -1,14 +1,90 @@
import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useRecoilCallback } from 'recoil';
type OpenActivityTargetInlineCellEditModeProps = {
recordPickerInstanceId: string;
activityTargetObjectRecords: ActivityTargetWithTargetRecord[];
};
export const useOpenActivityTargetInlineCellEditMode = () => {
const openActivityTargetInlineCellEditMode = ({
recordPickerInstanceId,
}: OpenActivityTargetInlineCellEditModeProps) => {
// eslint-disable-next-line no-console
console.log('openActivityTargetInlineCellEditMode', recordPickerInstanceId);
};
const { toggleClickOutsideListener: toggleRightDrawerClickOustideListener } =
useClickOutsideListener(RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID);
const { performSearch: multipleRecordPickerPerformSearch } =
useMultipleRecordPickerPerformSearch();
const openActivityTargetInlineCellEditMode = useRecoilCallback(
({ set, snapshot }) =>
({
recordPickerInstanceId,
activityTargetObjectRecords,
}: OpenActivityTargetInlineCellEditModeProps) => {
const objectMetadataItems = snapshot
.getLoadable(objectMetadataItemsState)
.getValue()
.filter(
(objectMetadataItem) =>
objectMetadataItem.isSearchable &&
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task &&
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note,
);
set(
multipleRecordPickerPickableMorphItemsComponentState.atomFamily({
instanceId: recordPickerInstanceId,
}),
activityTargetObjectRecords.map((activityTargetObjectRecord) => ({
recordId: activityTargetObjectRecord.targetObject.id,
objectMetadataId:
activityTargetObjectRecord.targetObjectMetadataItem.id,
isSelected: true,
isMatchingSearchFilter: true,
})),
);
set(
multipleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
{
instanceId: recordPickerInstanceId,
},
),
objectMetadataItems,
);
set(
multipleRecordPickerSearchFilterComponentState.atomFamily({
instanceId: recordPickerInstanceId,
}),
'',
);
toggleRightDrawerClickOustideListener(false);
multipleRecordPickerPerformSearch({
multipleRecordPickerInstanceId: recordPickerInstanceId,
forceSearchFilter: '',
forceSearchableObjectMetadataItems: objectMetadataItems,
forcePickableMorphItems: activityTargetObjectRecords.map(
(activityTargetObjectRecord) => ({
recordId: activityTargetObjectRecord.targetObject.id,
objectMetadataId:
activityTargetObjectRecord.targetObjectMetadataItem.id,
isSelected: true,
isMatchingSearchFilter: true,
}),
),
});
},
[multipleRecordPickerPerformSearch, toggleRightDrawerClickOustideListener],
);
return { openActivityTargetInlineCellEditMode };
};

View File

@ -0,0 +1,173 @@
import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
import { NoteTarget } from '@/activities/types/NoteTarget';
import { TaskTarget } from '@/activities/types/TaskTarget';
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isNull } from '@sniptt/guards';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared';
import { v4 } from 'uuid';
type UpdateActivityTargetFromInlineCellProps = {
recordPickerInstanceId: string;
morphItem: RecordPickerPickableMorphItem;
activityTargetWithTargetRecords: ActivityTargetWithTargetRecord[];
};
export const useUpdateActivityTargetFromInlineCell = ({
activityObjectNameSingular,
activityId,
}: {
activityObjectNameSingular:
| CoreObjectNameSingular.Note
| CoreObjectNameSingular.Task;
activityId: string;
}) => {
const { createOneRecord: createOneActivityTarget } = useCreateOneRecord<
NoteTarget | TaskTarget
>({
objectNameSingular: getJoinObjectNameSingular(activityObjectNameSingular),
});
const { deleteOneRecord: deleteOneActivityTarget } = useDeleteOneRecord({
objectNameSingular: getJoinObjectNameSingular(activityObjectNameSingular),
});
const setActivityFromStore = useSetRecoilState(
recordStoreFamilyState(activityId),
);
const updateActivityTargetFromInlineCell = useRecoilCallback(
({ snapshot }) =>
async ({
morphItem,
activityTargetWithTargetRecords,
}: UpdateActivityTargetFromInlineCellProps) => {
const targetObjectName =
activityObjectNameSingular === CoreObjectNameSingular.Task
? 'task'
: 'note';
const pickedObjectMetadataItem = snapshot
.getLoadable(objectMetadataItemsState)
.getValue()
.find(
(objectMetadataItem) =>
objectMetadataItem.id === morphItem.objectMetadataId,
);
if (!isDefined(pickedObjectMetadataItem)) {
throw new Error('Could not find object metadata item');
}
let activityTargetsAfterUpdate: (TaskTarget | NoteTarget)[] = [];
const existingActivityTarget = activityTargetWithTargetRecords.find(
(activityTarget) =>
activityTarget.targetObject.id === morphItem.recordId,
);
if (isDefined(existingActivityTarget)) {
activityTargetsAfterUpdate = activityTargetWithTargetRecords
.map((activityTarget) => {
if (
activityTarget.targetObject.id === morphItem.recordId &&
!morphItem.isSelected
) {
return undefined;
}
return activityTarget.activityTarget;
})
.filter(isDefined);
if (!morphItem.isSelected) {
await deleteOneActivityTarget(
existingActivityTarget.targetObject.id,
);
}
} else {
const targetRecord = snapshot
.getLoadable(recordStoreFamilyState(morphItem.recordId))
.getValue();
if (!isDefined(targetRecord)) {
return;
}
if (!morphItem.isSelected) {
return;
}
const activityTarget =
activityObjectNameSingular === CoreObjectNameSingular.Task
? {
id: v4(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
__typename: 'TaskTarget',
taskId: activityId,
task: {
id: activityId,
__typename: 'Task',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
[pickedObjectMetadataItem.nameSingular]: targetRecord,
}
: {
id: v4(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
__typename: 'NoteTarget',
noteId: activityId,
note: {
id: activityId,
__typename: 'Note',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
[pickedObjectMetadataItem.nameSingular]: targetRecord,
};
activityTargetsAfterUpdate = [
...activityTargetWithTargetRecords.map((activityTarget) => {
return activityTarget.activityTarget;
}),
activityTarget as NoteTarget | TaskTarget,
];
await createOneActivityTarget({
...activityTarget,
[targetObjectName]: undefined,
[pickedObjectMetadataItem.nameSingular]: undefined,
} as Partial<NoteTarget | TaskTarget>);
}
setActivityFromStore((currentActivity) => {
if (isNull(currentActivity)) {
return null;
}
return {
...currentActivity,
[`${targetObjectName}Targets`]: activityTargetsAfterUpdate,
};
});
},
[
activityId,
activityObjectNameSingular,
createOneActivityTarget,
deleteOneActivityTarget,
setActivityFromStore,
],
);
return { updateActivityTargetFromInlineCell };
};