From 49aac04b84684834d82cdef0fbbd8a0d02439837 Mon Sep 17 00:00:00 2001 From: Amlan Kumar Nandy <45410599+amlannandy@users.noreply.github.com> Date: Tue, 14 Apr 2026 14:24:14 +0530 Subject: [PATCH] fix: edit button not coming up on avatar right after image upload (#19596) ## Summary After uploading an image/file to the empty avatar field in the person tab, the edit icon next to the field would not appear until the browser was refreshed or another field was clicked. ### Root cause - When user clicks over the avatar field, `recordFieldListCellEditModePosition` is set to `globalIndex` - That is fine when a avatar already exists. But when there is no avatar already set, the native file picker is opened with no `onClose` handler attached. - So after the file upload is completed, `recordFieldListCellEditModePosition` is never reset to null. - `FieldsWidgetCellEditModePortal` stays anchored to the avatar file element - When the user hovers over the same field again, its hover portal tries to compete to anchor for the same element - So, `RecordInlineCellDisplayMode ` (the edit button) doesn't render ### Fix - Pass the `onClose` function through `openFieldInput` to `openFilesFieldInput` - `onClose` resets `recordFieldListCellEditModePosition` back to null, when the upload completes. ## Before https://github.com/user-attachments/assets/ac9318e9-5471-434c-8af3-5c20d0112460 ## After https://github.com/user-attachments/assets/0d064a7f-95ad-4b92-a9ee-d9570f360972 Fixes #19595 --------- Co-authored-by: Charles Bochet --- ...ecordBoardCardCellHoveredPortalContent.tsx | 1 + ...rdCalendarCardCellHoveredPortalContent.tsx | 1 + ...ecordFieldListCellHoveredPortalContent.tsx | 1 + .../ui/hooks/useOpenFieldInputEditMode.ts | 3 + .../input/hooks/useOpenFilesFieldInput.tsx | 67 ++----------------- .../hooks/useOpenRecordTableCell.ts | 18 +++++ 6 files changed, 29 insertions(+), 62 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx index 643bda1a44..c50f93d40b 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx @@ -48,6 +48,7 @@ export const RecordBoardCardCellHoveredPortalContent = () => { fieldDefinition, recordId, prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX, + onFileUploadClose: () => setRecordBoardCardEditModePosition(null), }); } }; diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardCellHoveredPortalContent.tsx b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardCellHoveredPortalContent.tsx index 32aa0906d7..6be303c0c6 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardCellHoveredPortalContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardCellHoveredPortalContent.tsx @@ -48,6 +48,7 @@ export const RecordCalendarCardCellHoveredPortalContent = () => { fieldDefinition, recordId, prefix: RECORD_CALENDAR_CARD_INPUT_ID_PREFIX, + onFileUploadClose: () => setRecordCalendarCardEditModePosition(null), }); } }; diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortalContent.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortalContent.tsx index cfa253a5be..d38697116c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortalContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortalContent.tsx @@ -52,6 +52,7 @@ export const RecordFieldListCellHoveredPortalContent = () => { fieldDefinition, recordId, prefix: instanceId, + onFileUploadClose: () => setRecordFieldListCellEditModePosition(null), }); } }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useOpenFieldInputEditMode.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useOpenFieldInputEditMode.ts index 08bbf7be08..aecaf945ed 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useOpenFieldInputEditMode.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useOpenFieldInputEditMode.ts @@ -66,10 +66,12 @@ export const useOpenFieldInputEditMode = () => { fieldDefinition, recordId, prefix, + onFileUploadClose, }: { fieldDefinition: FieldDefinition; recordId: string; prefix?: string; + onFileUploadClose?: () => void; }) => { const objectMetadataItems = store.get(objectMetadataItemsSelector.atom); @@ -106,6 +108,7 @@ export const useOpenFieldInputEditMode = () => { updateOneRecordInput: updateInput, }); }, + onFileUploadClose, fieldDefinition: { metadata: { settings: fieldDefinition.metadata.settings ?? undefined, diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx index bddc7a4266..89b96433a0 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx @@ -4,16 +4,10 @@ import { uploadMultipleFiles } from '@/object-record/record-field/ui/meta-types/ import { filesFieldUploadState } from '@/object-record/record-field/ui/states/filesFieldUploadState'; import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; -import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix'; -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; -import { useRemoveLastFocusItemFromFocusStackByComponentType } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; import { useStore } from 'jotai'; import { useLingui } from '@lingui/react/macro'; import { useCallback } from 'react'; @@ -24,13 +18,6 @@ export const useOpenFilesFieldInput = () => { const { openFileUpload } = useFileUpload(); const { uploadFile } = useUploadFilesFieldFile(); const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); - const { removeLastFocusItemFromFocusStackByComponentType } = - useRemoveLastFocusItemFromFocusStackByComponentType(); - const { goBackToPreviousDropdownFocusId } = - useGoBackToPreviousDropdownFocusId(); - const recordTableId = useAvailableComponentInstanceId( - RecordTableComponentInstanceContext, - ); const { enqueueErrorSnackBar } = useSnackBar(); const { t } = useLingui(); const store = useStore(); @@ -42,7 +29,7 @@ export const useOpenFilesFieldInput = () => { recordId, prefix, updateRecord, - onClose, + onFileUploadClose, fieldDefinition, }: { fieldName: string; @@ -50,7 +37,7 @@ export const useOpenFilesFieldInput = () => { recordId: string; prefix?: string; updateRecord: (updateInput: Record) => void; - onClose?: () => void; + onFileUploadClose?: () => void; fieldDefinition?: { metadata: { settings?: { @@ -86,8 +73,6 @@ export const useOpenFilesFieldInput = () => { return; } - const isTableContext = prefix === RECORD_TABLE_CELL_INPUT_ID_PREFIX; - const maxNumberOfValues = fieldDefinition?.metadata?.settings?.maxNumberOfValues ?? MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES; @@ -112,20 +97,7 @@ export const useOpenFilesFieldInput = () => { null, ); - if (isTableContext && isDefined(recordTableId)) { - store.set( - recordTableCellEditModePositionComponentState.atomFamily({ - instanceId: recordTableId, - }), - null, - ); - goBackToPreviousDropdownFocusId(); - removeLastFocusItemFromFocusStackByComponentType({ - componentType: FocusComponentType.OPENED_FIELD_INPUT, - }); - } else { - onClose?.(); - } + onFileUploadClose?.(); return; } @@ -152,20 +124,7 @@ export const useOpenFilesFieldInput = () => { null, ); - if (isTableContext && isDefined(recordTableId)) { - store.set( - recordTableCellEditModePositionComponentState.atomFamily({ - instanceId: recordTableId, - }), - null, - ); - goBackToPreviousDropdownFocusId(); - removeLastFocusItemFromFocusStackByComponentType({ - componentType: FocusComponentType.OPENED_FIELD_INPUT, - }); - } else { - onClose?.(); - } + onFileUploadClose?.(); } }, onCancel: () => { @@ -174,20 +133,7 @@ export const useOpenFilesFieldInput = () => { null, ); - if (isTableContext && isDefined(recordTableId)) { - store.set( - recordTableCellEditModePositionComponentState.atomFamily({ - instanceId: recordTableId, - }), - null, - ); - goBackToPreviousDropdownFocusId(); - removeLastFocusItemFromFocusStackByComponentType({ - componentType: FocusComponentType.OPENED_FIELD_INPUT, - }); - } else { - onClose?.(); - } + onFileUploadClose?.(); }, }); }, @@ -195,9 +141,6 @@ export const useOpenFilesFieldInput = () => { openFileUpload, uploadFile, pushFocusItemToFocusStack, - recordTableId, - goBackToPreviousDropdownFocusId, - removeLastFocusItemFromFocusStackByComponentType, enqueueErrorSnackBar, t, store, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts index 383e379ecf..de4267f07c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts @@ -24,6 +24,9 @@ import { useFocusedRecordTableRow } from '@/object-record/record-table/hooks/use import { useFocusRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useFocusRecordTableCell'; import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; +import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; +import { useRemoveLastFocusItemFromFocusStackByComponentType } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType'; +import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState'; import { useStore } from 'jotai'; import { useCallback } from 'react'; @@ -61,6 +64,12 @@ export const useOpenRecordTableCell = (recordTableId: string) => { const { openFieldInput } = useOpenFieldInputEditMode(); + const { goBackToPreviousDropdownFocusId } = + useGoBackToPreviousDropdownFocusId(); + + const { removeLastFocusItemFromFocusStackByComponentType } = + useRemoveLastFocusItemFromFocusStackByComponentType(); + const { activateRecordTableRow, deactivateRecordTableRow } = useActiveRecordTableRow(recordTableId); @@ -137,6 +146,13 @@ export const useOpenRecordTableCell = (recordTableId: string) => { fieldDefinition, recordId, prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX, + onFileUploadClose: () => { + setRecordTableCellEditModePosition(null); + goBackToPreviousDropdownFocusId(); + removeLastFocusItemFromFocusStackByComponentType({ + componentType: FocusComponentType.OPENED_FIELD_INPUT, + }); + }, }); setRecordTableCellEditModePosition(cellPosition); @@ -170,6 +186,8 @@ export const useOpenRecordTableCell = (recordTableId: string) => { setDragSelectionStartEnabled, openFieldInput, setRecordTableCellEditModePosition, + goBackToPreviousDropdownFocusId, + removeLastFocusItemFromFocusStackByComponentType, initDraftValue, toggleClickOutside, setActiveDropdownFocusIdAndMemorizePrevious,