diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useRecordFieldInput.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useRecordFieldInput.ts index 3e7d1b2117..e63b2363e0 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useRecordFieldInput.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/useRecordFieldInput.ts @@ -7,6 +7,19 @@ export const useRecordFieldInput = () => { const recordFieldInputDraftValueCallbackState = useRecoilComponentCallbackState(recordFieldInputDraftValueComponentState); + const getLatestDraftValue = useRecoilCallback( + ({ snapshot }) => + (instanceId: string) => + snapshot + .getLoadable( + recordFieldInputDraftValueComponentState.atomFamily({ + instanceId, + }), + ) + .getValue() as FieldInputDraftValue, + [], + ); + const setDraftValue = useRecoilCallback( ({ set }) => (newValue: unknown) => { @@ -30,6 +43,7 @@ export const useRecordFieldInput = () => { }; return { + getLatestDraftValue, setDraftValue, isDraftValueEmpty, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useArrayField.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useArrayField.ts index a9a1d76892..61020463a9 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useArrayField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useArrayField.ts @@ -24,7 +24,8 @@ export const useArrayField = () => { }), ); - const { setDraftValue } = useRecordFieldInput(); + const { getLatestDraftValue, setDraftValue } = + useRecordFieldInput(); const draftValue = useRecoilComponentValue( recordFieldInputDraftValueComponentState, @@ -33,8 +34,9 @@ export const useArrayField = () => { return { fieldValue, fieldDefinition, - setFieldValue, draftValue, + getLatestDraftValue, + setFieldValue, setDraftValue, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsField.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsField.ts index d680c98462..b5e217b2e3 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsField.ts @@ -26,7 +26,8 @@ export const useEmailsField = () => { }), ); - const { setDraftValue } = useRecordFieldInput(); + const { getLatestDraftValue, setDraftValue } = + useRecordFieldInput(); const draftValue = useRecoilComponentValue( recordFieldInputDraftValueComponentState, @@ -36,6 +37,7 @@ export const useEmailsField = () => { fieldDefinition, fieldValue, draftValue, + getLatestDraftValue, setDraftValue, setFieldValue, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksField.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksField.ts index 17f09c75ee..97444c19c5 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksField.ts @@ -26,7 +26,8 @@ export const useLinksField = () => { }), ); - const { setDraftValue } = useRecordFieldInput(); + const { getLatestDraftValue, setDraftValue } = + useRecordFieldInput(); const draftValue = useRecoilComponentValue( recordFieldInputDraftValueComponentState, @@ -36,6 +37,7 @@ export const useLinksField = () => { fieldDefinition, fieldValue, draftValue, + getLatestDraftValue, setDraftValue, setFieldValue, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesField.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesField.ts index 8158d3441b..f0dbe220bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesField.ts @@ -26,7 +26,8 @@ export const usePhonesField = () => { }), ); - const { setDraftValue } = useRecordFieldInput(); + const { getLatestDraftValue, setDraftValue } = + useRecordFieldInput(); const draftValue = useRecoilComponentValue( recordFieldInputDraftValueComponentState, @@ -36,6 +37,7 @@ export const usePhonesField = () => { fieldDefinition, fieldValue, draftValue, + getLatestDraftValue, setDraftValue, setFieldValue, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/ArrayFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/ArrayFieldInput.tsx index d3f28a0639..205842967b 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/ArrayFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/ArrayFieldInput.tsx @@ -2,16 +2,22 @@ import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts import { useArrayField } from '@/object-record/record-field/ui/meta-types/hooks/useArrayField'; import { ArrayFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/ArrayFieldMenuItem'; import { MultiItemFieldInput } from '@/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput'; +import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { arraySchema } from '@/object-record/record-field/ui/types/guards/isFieldArrayValue'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useContext, useMemo } from 'react'; import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; export const ArrayFieldInput = () => { - const { setDraftValue, draftValue, fieldDefinition } = useArrayField(); + const { getLatestDraftValue, setDraftValue, draftValue, fieldDefinition } = + useArrayField(); const { onEscape, onClickOutside } = useContext(FieldInputEventContext); + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordFieldComponentInstanceContext, + ); const arrayItems = useMemo>( () => (Array.isArray(draftValue) ? draftValue : []), @@ -32,7 +38,8 @@ export const ArrayFieldInput = () => { _newValue: any, event: MouseEvent | TouchEvent, ) => { - onClickOutside?.({ newValue: draftValue, event }); + const latestDraftValue = getLatestDraftValue(instanceId); + onClickOutside?.({ newValue: latestDraftValue, event }); }; const handleEscape = (_newValue: any) => { diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/EmailsFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/EmailsFieldInput.tsx index 8456c0d79a..2b023f4521 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/EmailsFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/EmailsFieldInput.tsx @@ -1,9 +1,11 @@ import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; import { useEmailsField } from '@/object-record/record-field/ui/meta-types/hooks/useEmailsField'; import { EmailsFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/EmailsFieldMenuItem'; +import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState'; import { emailsSchema } from '@/object-record/record-field/ui/types/guards/isFieldEmailsValue'; import { emailSchema } from '@/object-record/record-field/ui/validation-schemas/emailSchema'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useLingui } from '@lingui/react/macro'; import { useCallback, useContext, useMemo } from 'react'; @@ -14,11 +16,15 @@ import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; import { MultiItemFieldInput } from './MultiItemFieldInput'; export const EmailsFieldInput = () => { - const { setDraftValue, draftValue, fieldDefinition } = useEmailsField(); + const { getLatestDraftValue, setDraftValue, draftValue, fieldDefinition } = + useEmailsField(); const { copyToClipboard } = useCopyToClipboard(); const { t } = useLingui(); const { onEscape, onClickOutside } = useContext(FieldInputEventContext); + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordFieldComponentInstanceContext, + ); const emails = useMemo( () => @@ -72,7 +78,8 @@ export const EmailsFieldInput = () => { _newValue: any, event: MouseEvent | TouchEvent, ) => { - onClickOutside?.({ newValue: draftValue, event }); + const latestDraftValue = getLatestDraftValue(instanceId); + onClickOutside?.({ newValue: latestDraftValue, event }); }; const handleEscape = (_newValue: any) => { diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/LinksFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/LinksFieldInput.tsx index ebeff23e3b..911a720667 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/LinksFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/LinksFieldInput.tsx @@ -2,8 +2,10 @@ import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts import { useLinksField } from '@/object-record/record-field/ui/meta-types/hooks/useLinksField'; import { LinksFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/LinksFieldMenuItem'; import { getFieldLinkDefinedLinks } from '@/object-record/record-field/ui/meta-types/input/utils/getFieldLinkDefinedLinks'; +import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState'; import { linksSchema } from '@/object-record/record-field/ui/types/guards/isFieldLinksValue'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useContext, useMemo } from 'react'; import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants'; @@ -12,7 +14,11 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { MultiItemFieldInput } from './MultiItemFieldInput'; export const LinksFieldInput = () => { - const { draftValue, fieldDefinition, setDraftValue } = useLinksField(); + const { getLatestDraftValue, draftValue, fieldDefinition, setDraftValue } = + useLinksField(); + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordFieldComponentInstanceContext, + ); const { onEscape, onClickOutside } = useContext(FieldInputEventContext); @@ -55,7 +61,8 @@ export const LinksFieldInput = () => { _newValue: any, event: MouseEvent | TouchEvent, ) => { - onClickOutside?.({ newValue: draftValue, event }); + const latestDraftValue = getLatestDraftValue(instanceId); + onClickOutside?.({ newValue: latestDraftValue, event }); }; const handleEscape = (_newValue: any) => { diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx index 41b4dfd289..43764b4942 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx @@ -74,13 +74,7 @@ export const MultiItemFieldInput = ({ useListenClickOutside({ refs: [containerRef], callback: (event) => { - const isEditing = inputValue !== ''; - const isPrimaryItem = items.length === 0; - - if (isEditing && isPrimaryItem) { - handleSubmitInput(); - } - + handleSubmitInput(); onClickOutside?.(items, event); }, listenerId: instanceId, diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/PhonesFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/PhonesFieldInput.tsx index 69ae037f61..c52eff4cd2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/PhonesFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/PhonesFieldInput.tsx @@ -12,8 +12,10 @@ import { MultiItemFieldInput } from './MultiItemFieldInput'; import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; import { createPhonesFromFieldValue } from '@/object-record/record-field/ui/meta-types/input/utils/phonesUtils'; +import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { phonesSchema } from '@/object-record/record-field/ui/types/guards/isFieldPhonesValue'; import { PhoneCountryPickerDropdownButton } from '@/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownButton'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { css } from '@emotion/react'; import { useContext } from 'react'; import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants'; @@ -73,11 +75,15 @@ const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)` `; export const PhonesFieldInput = () => { - const { fieldDefinition, setDraftValue, draftValue } = usePhonesField(); + const { getLatestDraftValue, fieldDefinition, setDraftValue, draftValue } = + usePhonesField(); const { onEscape, onClickOutside } = useContext(FieldInputEventContext); const phones = createPhonesFromFieldValue(draftValue); + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordFieldComponentInstanceContext, + ); const defaultCountry = stripSimpleQuotesFromString( fieldDefinition?.defaultValue?.primaryPhoneCountryCode, @@ -131,7 +137,8 @@ export const PhonesFieldInput = () => { _newValue: any, event: MouseEvent | TouchEvent, ) => { - onClickOutside?.({ newValue: draftValue, event }); + const latestDraftValue = getLatestDraftValue(instanceId); + onClickOutside?.({ newValue: latestDraftValue, event }); }; const handleEscape = (_newValue: any) => {