Files
twenty/packages/twenty-front/src/modules/activities/inline-cell/hooks/useOpenActivityTargetCellEditMode.ts
Lucas Bordeau 3cee2b796f Fixed record picker loading flickering (#12736)
This PR solves a flickering effect on record pickers on the different
loading state they can be in.

It was designed with @Bonapara to settle on a nice UX feeling.

## Before

With fast network (local) :


https://github.com/user-attachments/assets/58899934-c705-4b44-b7f6-289045032c11

With slow network : 


https://github.com/user-attachments/assets/9fb18d86-9da6-4e5d-a83f-00c810fab2dc

## After


https://github.com/user-attachments/assets/f4abb40f-5d42-4c46-88ab-aaef4f883f7f

Fixes https://github.com/twentyhq/twenty/issues/12680

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-06-24 12:15:50 +02:00

113 lines
4.7 KiB
TypeScript

import { ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useMultipleRecordPickerOpen } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerOpen';
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 { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { useRecoilCallback } from 'recoil';
type OpenActivityTargetCellEditModeProps = {
recordPickerInstanceId: string;
activityTargetObjectRecords: ActivityTargetWithTargetRecord[];
};
// TODO: deprecate this once we are supporting one to many through relations
export const useOpenActivityTargetCellEditMode = () => {
const { performSearch: multipleRecordPickerPerformSearch } =
useMultipleRecordPickerPerformSearch();
const { openMultipleRecordPicker } = useMultipleRecordPickerOpen();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const openActivityTargetCellEditMode = useRecoilCallback(
({ set, snapshot }) =>
({
recordPickerInstanceId,
activityTargetObjectRecords,
}: OpenActivityTargetCellEditModeProps) => {
const objectMetadataItems = snapshot
.getLoadable(objectMetadataItemsState)
.getValue()
.filter(
(objectMetadataItem) =>
objectMetadataItem.isSearchable &&
objectMetadataItem.isActive &&
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task &&
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note &&
objectMetadataItem.nameSingular !==
CoreObjectNameSingular.WorkspaceMember,
);
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,
}),
'',
);
openMultipleRecordPicker(recordPickerInstanceId);
multipleRecordPickerPerformSearch({
multipleRecordPickerInstanceId: recordPickerInstanceId,
forceSearchFilter: '',
forceSearchableObjectMetadataItems: objectMetadataItems,
forcePickableMorphItems: activityTargetObjectRecords.map(
(activityTargetObjectRecord) => ({
recordId: activityTargetObjectRecord.targetObject.id,
objectMetadataId:
activityTargetObjectRecord.targetObjectMetadataItem.id,
isSelected: true,
isMatchingSearchFilter: true,
}),
),
});
pushFocusItemToFocusStack({
focusId: recordPickerInstanceId,
component: {
type: FocusComponentType.DROPDOWN,
instanceId: recordPickerInstanceId,
},
hotkeyScope: {
scope: DropdownHotkeyScope.Dropdown,
},
memoizeKey: recordPickerInstanceId,
});
},
[
multipleRecordPickerPerformSearch,
openMultipleRecordPicker,
pushFocusItemToFocusStack,
],
);
return { openActivityTargetCellEditMode };
};