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 <charles@twenty.com>
This commit is contained in:
Amlan Kumar Nandy
2026-04-14 14:24:14 +05:30
committed by GitHub
parent 40c6c63bf5
commit 49aac04b84
6 changed files with 29 additions and 62 deletions
@@ -48,6 +48,7 @@ export const RecordBoardCardCellHoveredPortalContent = () => {
fieldDefinition,
recordId,
prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
onFileUploadClose: () => setRecordBoardCardEditModePosition(null),
});
}
};
@@ -48,6 +48,7 @@ export const RecordCalendarCardCellHoveredPortalContent = () => {
fieldDefinition,
recordId,
prefix: RECORD_CALENDAR_CARD_INPUT_ID_PREFIX,
onFileUploadClose: () => setRecordCalendarCardEditModePosition(null),
});
}
};
@@ -52,6 +52,7 @@ export const RecordFieldListCellHoveredPortalContent = () => {
fieldDefinition,
recordId,
prefix: instanceId,
onFileUploadClose: () => setRecordFieldListCellEditModePosition(null),
});
}
};
@@ -66,10 +66,12 @@ export const useOpenFieldInputEditMode = () => {
fieldDefinition,
recordId,
prefix,
onFileUploadClose,
}: {
fieldDefinition: FieldDefinition<FieldMetadata>;
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,
@@ -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<string, unknown>) => 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,
@@ -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,