Files
twenty/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts
T
Charles Bochet d81f006979 Rename Jotai state hooks and utilities to consistent Atom-based naming (#18209)
## Summary

Rename all Jotai-based state management hooks and utilities to a
consistent naming convention that:
1. Removes legacy Recoil naming (`useRecoilXxxV2`, `createXxxV2`)
2. Always includes `Atom` in the name to distinguish from jotai native
hooks
3. Follows a consistent ordering: `Atom` → `Component` → `Family` →
`Type` (`State`/`Selector`)
4. Includes the type qualifier (`State` or `Selector`) in all
value-reading hooks to avoid naming conflicts with jotai's native
`useAtomValue`

## Naming Convention

**Hooks**:
`use[Set]Atom[Component][Family][State|Selector][Value|State|CallbackState]`
**Utils**: `createAtom[Component][Family][State|Selector]`

## Changes

### Hooks renamed (definition files + all usages across ~1,500 files):

| Old Name (Recoil) | Intermediate (Atom) | Final Name |
|---|---|---|
| `useRecoilValueV2` | `useAtomValue` | **`useAtomStateValue`** |
| `useRecoilStateV2` | `useAtomState` | `useAtomState` |
| `useSetRecoilStateV2` | `useSetAtomState` | `useSetAtomState` |
| `useFamilyRecoilValueV2` | `useFamilyAtomValue` |
**`useAtomFamilyStateValue`** |
| `useSetFamilyRecoilStateV2` | `useSetFamilyAtomState` |
**`useSetAtomFamilyState`** |
| `useFamilySelectorValueV2` | `useFamilySelectorValue` |
**`useAtomFamilySelectorValue`** |
| `useFamilySelectorStateV2` | `useFamilySelectorState` |
**`useAtomFamilySelectorState`** |
| `useRecoilComponentValueV2` | `useAtomComponentValue` |
**`useAtomComponentStateValue`** |
| `useRecoilComponentStateV2` | `useAtomComponentState` |
`useAtomComponentState` |
| `useSetRecoilComponentStateV2` | `useSetAtomComponentState` |
`useSetAtomComponentState` |
| `useRecoilComponentFamilyValueV2` | `useAtomComponentFamilyValue` |
**`useAtomComponentFamilyStateValue`** |
| `useRecoilComponentFamilyStateV2` | `useAtomComponentFamilyState` |
`useAtomComponentFamilyState` |
| `useSetRecoilComponentFamilyStateV2` |
`useSetAtomComponentFamilyState` | `useSetAtomComponentFamilyState` |
| `useRecoilComponentSelectorValueV2` | `useAtomComponentSelectorValue`
| `useAtomComponentSelectorValue` |
| `useRecoilComponentFamilySelectorValueV2` |
`useAtomComponentFamilySelectorValue` |
`useAtomComponentFamilySelectorValue` |
| `useRecoilComponentStateCallbackStateV2` |
`useAtomComponentStateCallbackState` |
`useAtomComponentStateCallbackState` |
| `useRecoilComponentSelectorCallbackStateV2` |
`useAtomComponentSelectorCallbackState` |
`useAtomComponentSelectorCallbackState` |
| `useRecoilComponentFamilyStateCallbackStateV2` |
`useAtomComponentFamilyStateCallbackState` |
`useAtomComponentFamilyStateCallbackState` |
| `useRecoilComponentFamilySelectorCallbackStateV2` |
`useAtomComponentFamilySelectorCallbackState` |
`useAtomComponentFamilySelectorCallbackState` |

### Utilities renamed:

| Old Name (Recoil) | Final Name |
|---|---|
| `createStateV2` | **`createAtomState`** |
| `createFamilyStateV2` | **`createAtomFamilyState`** |
| `createSelectorV2` | **`createAtomSelector`** |
| `createFamilySelectorV2` | **`createAtomFamilySelector`** |
| `createWritableSelectorV2` | **`createAtomWritableSelector`** |
| `createWritableFamilySelectorV2` |
**`createAtomWritableFamilySelector`** |
| `createComponentStateV2` | **`createAtomComponentState`** |
| `createComponentFamilyStateV2` | **`createAtomComponentFamilyState`**
|
| `createComponentSelectorV2` | **`createAtomComponentSelector`** |
| `createComponentFamilySelectorV2` |
**`createAtomComponentFamilySelector`** |

## Details

- All definition files renamed to match new convention
- All import paths and usages updated across the entire `twenty-front`
package
- Jotai's native `useAtomValue` is aliased as `useJotaiAtomValue` in the
wrapper hook to avoid collision
- Legacy Recoil files in `state/utils/` and `component-state/utils/`
left untouched (separate naming scope)
- Typecheck passes cleanly
2026-02-25 01:55:46 +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 { 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 { 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 setActivityFromStore = useSetAtomFamilyState(
recordStoreFamilyState,
activityId,
);
const updateActivityTargetFromCell = useCallback(
async ({
morphItem,
activityTargetWithTargetRecords,
}: UpdateActivityTargetFromCellProps) => {
const targetObjectName =
activityObjectNameSingular === CoreObjectNameSingular.Task
? 'task'
: 'note';
const objectMetadataItems = store.get(objectMetadataItemsState.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
.map((activityTarget) => {
if (
activityTarget.targetObject.id === morphItem.recordId &&
!morphItem.isSelected
) {
return undefined;
}
return activityTarget.activityTarget;
})
.filter(isDefined);
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);
}
setActivityFromStore((currentActivity) => {
if (!isDefined(currentActivity)) {
return null;
}
return {
...currentActivity,
[`${targetObjectName}Targets`]: activityTargetsAfterUpdate,
};
});
},
[
store,
activityId,
activityObjectNameSingular,
createOneActivityTarget,
deleteOneActivityTarget,
isMorphRelation,
setActivityFromStore,
],
);
return { updateActivityTargetFromCell };
};