Files
twenty/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts
T
Charles BochetandGitHub 1be87eb97b chore: frontend dead code removal and naming cleanup (#18690)
## Summary

- **Delete 10 unused files**: 7 hooks (`useWorkflowRunUnsafe`,
`useGetViewById`, `useCreateViewFieldGroup`, `useDeleteViewFieldGroup`,
`useUpdateViewFieldGroup`, `useCreateManyViewFieldGroups`,
`useMoveViewColumns` + test), 1 component (`SettingsSummaryCard`), 1
utility (`createEventContext`)
- **Rename `objectMetadataItemsState` → `objectMetadataItemsSelector`**
across ~85 files to accurately reflect it is a derived selector (via
`createAtomSelector`), not a base Jotai atom

## Details

### Dead code removed

| Type | Name | Reason |
|------|------|--------|
| Hook | `useWorkflowRunUnsafe` | Never imported — duplicate of
`useWorkflowRun` without schema validation |
| Hook | `useGetViewById` | Never imported — `useViewById` is used
instead |
| Hook | `useCreateViewFieldGroup` | Never imported — CRUD done via
`usePerformViewFieldGroupAPIPersist` |
| Hook | `useDeleteViewFieldGroup` | Same as above |
| Hook | `useUpdateViewFieldGroup` | Same as above |
| Hook | `useCreateManyViewFieldGroups` | Same as above |
| Hook | `useMoveViewColumns` | Only imported by its own test — no
production usage |
| Component | `SettingsSummaryCard` | Never imported anywhere |
| Utility | `createEventContext` | Never imported anywhere |

### Rename

`objectMetadataItemsState` is created via `createAtomSelector` (it
derives from `objectMetadataItemsWithFieldsSelector`), so naming it
`*State` is misleading. Renamed to `objectMetadataItemsSelector` for
consistency with sibling selectors like
`objectMetadataItemsByNamePluralMapSelector`.
2026-03-17 00:49:37 +01:00

221 lines
7.4 KiB
TypeScript

import { type ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
import { type NoteTarget } from '@/activities/types/NoteTarget';
import { type TaskTarget } from '@/activities/types/TaskTarget';
import { getActivityTargetFieldNameForObject } from '@/activities/utils/getActivityTargetFieldNameForObject';
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { searchRecordStoreFamilyState } from '@/object-record/record-picker/multiple-record-picker/states/searchRecordStoreComponentFamilyState';
import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
type UpdateActivityTargetFromCellProps = {
recordPickerInstanceId: string;
morphItem: RecordPickerPickableMorphItem;
activityTargetWithTargetRecords: ActivityTargetWithTargetRecord[];
};
// TODO: deprecate this hook once we implement one-to-many relation through
export const useUpdateActivityTargetFromCell = ({
activityObjectNameSingular,
activityId,
}: {
activityObjectNameSingular:
| CoreObjectNameSingular.Note
| CoreObjectNameSingular.Task;
activityId: string;
}) => {
const joinObjectNameSingular = getJoinObjectNameSingular(
activityObjectNameSingular,
);
const { createOneRecord: createOneActivityTarget } = useCreateOneRecord<
NoteTarget | TaskTarget
>({
objectNameSingular:
joinObjectNameSingular === ''
? activityObjectNameSingular
: joinObjectNameSingular,
shouldMatchRootQueryFilter: true,
});
const { deleteOneRecord: deleteOneActivityTarget } = useDeleteOneRecord({
objectNameSingular:
joinObjectNameSingular === ''
? activityObjectNameSingular
: joinObjectNameSingular,
});
const isNoteTargetMigrated = useIsFeatureEnabled(
FeatureFlagKey.IS_NOTE_TARGET_MIGRATED,
);
const isTaskTargetMigrated = useIsFeatureEnabled(
FeatureFlagKey.IS_TASK_TARGET_MIGRATED,
);
const isMorphRelation =
activityObjectNameSingular === CoreObjectNameSingular.Task
? isTaskTargetMigrated
: isNoteTargetMigrated;
const store = useStore();
const setRecordStore = useSetAtomFamilyState(
recordStoreFamilyState,
activityId,
);
const updateActivityTargetFromCell = useCallback(
async ({
morphItem,
activityTargetWithTargetRecords,
}: UpdateActivityTargetFromCellProps) => {
const targetObjectName =
activityObjectNameSingular === CoreObjectNameSingular.Task
? 'task'
: 'note';
const objectMetadataItems = store.get(objectMetadataItemsSelector.atom);
const pickedObjectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id === morphItem.objectMetadataId,
);
if (!isDefined(pickedObjectMetadataItem)) {
throw new Error('Could not find object metadata item');
}
const targetFieldName = getActivityTargetFieldNameForObject({
activityObjectNameSingular,
targetObjectMetadataId: morphItem.objectMetadataId,
objectMetadataItems,
isMorphRelation,
});
if (!isDefined(targetFieldName)) {
throw new Error(
`Could not find field on activity target for object ${pickedObjectMetadataItem.nameSingular}`,
);
}
let activityTargetsAfterUpdate: (TaskTarget | NoteTarget)[] = [];
const existingActivityTarget = activityTargetWithTargetRecords.find(
(activityTarget) =>
activityTarget.targetObject.id === morphItem.recordId,
);
if (isDefined(existingActivityTarget)) {
activityTargetsAfterUpdate = activityTargetWithTargetRecords.flatMap(
(activityTarget) => {
if (
activityTarget.targetObject.id === morphItem.recordId &&
!morphItem.isSelected
) {
return [];
}
return [activityTarget.activityTarget];
},
);
if (!morphItem.isSelected) {
await deleteOneActivityTarget(
existingActivityTarget.activityTarget.id,
);
}
} else {
const searchRecord = store.get(
searchRecordStoreFamilyState.atomFamily(morphItem.recordId),
);
if (!isDefined(searchRecord) || !isDefined(searchRecord?.record)) {
return;
}
if (!morphItem.isSelected) {
return;
}
const targetRecord = searchRecord.record;
const activityTarget: NoteTarget | TaskTarget =
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(),
},
[targetFieldName]: 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(),
},
[targetFieldName]: targetRecord,
};
activityTargetsAfterUpdate = [
...activityTargetWithTargetRecords.map((activityTarget) => {
return activityTarget.activityTarget;
}),
activityTarget,
];
const activityTargetInput: Partial<NoteTarget | TaskTarget> = {
...activityTarget,
[targetObjectName]: undefined,
[targetFieldName]: undefined,
[`${targetFieldName}Id`]: morphItem.recordId,
};
await createOneActivityTarget(activityTargetInput);
}
setRecordStore((currentActivity) => {
if (!isDefined(currentActivity)) {
return null;
}
return {
...currentActivity,
[`${targetObjectName}Targets`]: activityTargetsAfterUpdate,
};
});
},
[
store,
activityId,
activityObjectNameSingular,
createOneActivityTarget,
deleteOneActivityTarget,
isMorphRelation,
setRecordStore,
],
);
return { updateActivityTargetFromCell };
};