Removed use-context-selector completely (#12139)

This PR removes use-context-selector completely, so that any bug
associated with state synchronization between recoil and
use-context-selector disappears.

There might be a slight performance decrease on the table, but since we
have already improved the average performance per line by a lot, and
that the performance bottleneck right now is the fetch more logic and
the windowing solution we use, it is not relevant.

Also the DX has become so hindered by this parallel state logic recently
(think [cache
invalidation](https://martinfowler.com/bliki/TwoHardThings.html)), that
the main benefit we gain from this removal is the DX improvement.

Fixes https://github.com/twentyhq/twenty/issues/12123
Fixes https://github.com/twentyhq/twenty/issues/12109
This commit is contained in:
Lucas Bordeau
2025-05-20 13:35:28 +02:00
committed by GitHub
parent 9ba24b3654
commit 0553f58c52
30 changed files with 408 additions and 697 deletions

View File

@ -191,7 +191,6 @@
"tsup": "^8.2.4", "tsup": "^8.2.4",
"type-fest": "4.10.1", "type-fest": "4.10.1",
"typescript": "5.3.3", "typescript": "5.3.3",
"use-context-selector": "^2.0.0",
"use-debounce": "^10.0.0", "use-debounce": "^10.0.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vite-tsconfig-paths": "^4.2.1", "vite-tsconfig-paths": "^4.2.1",

View File

@ -1,5 +1,4 @@
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent'; import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useEffect } from 'react'; import { useEffect } from 'react';
@ -11,7 +10,6 @@ export const CalendarEventDetailsEffect = ({
record, record,
}: CalendarEventDetailsEffectProps) => { }: CalendarEventDetailsEffectProps) => {
const { upsertRecords } = useUpsertRecordsInStore(); const { upsertRecords } = useUpsertRecordsInStore();
const setRecordValueInContextSelector = useSetRecordValue();
useEffect(() => { useEffect(() => {
if (!record) { if (!record) {
@ -19,8 +17,7 @@ export const CalendarEventDetailsEffect = ({
} }
upsertRecords([record]); upsertRecords([record]);
setRecordValueInContextSelector(record.id, record); }, [record, upsertRecords]);
}, [record, upsertRecords, setRecordValueInContextSelector]);
return <></>; return <></>;
}; };

View File

@ -5,7 +5,6 @@ import { EventFieldDiffValue } from '@/activities/timeline-activities/rows/main-
import { EventFieldDiffValueEffect } from '@/activities/timeline-activities/rows/main-object/components/EventFieldDiffValueEffect'; import { EventFieldDiffValueEffect } from '@/activities/timeline-activities/rows/main-object/components/EventFieldDiffValueEffect';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
type EventFieldDiffProps = { type EventFieldDiffProps = {
@ -54,29 +53,27 @@ export const EventFieldDiff = ({
isObjectEmpty(diffRecord)); isObjectEmpty(diffRecord));
return ( return (
<RecordFieldValueSelectorContextProvider> <StyledEventFieldDiffContainer>
<StyledEventFieldDiffContainer> <EventFieldDiffLabel fieldMetadataItem={fieldMetadataItem} />
<EventFieldDiffLabel fieldMetadataItem={fieldMetadataItem} /> {isUpdatedToEmpty ? (
{isUpdatedToEmpty ? ( <StyledEmptyValue>
<StyledEmptyValue> <Trans>Empty</Trans>
<Trans>Empty</Trans> </StyledEmptyValue>
</StyledEmptyValue> ) : (
) : ( <>
<> <EventFieldDiffValueEffect
<EventFieldDiffValueEffect diffArtificialRecordStoreId={diffArtificialRecordStoreId}
diffArtificialRecordStoreId={diffArtificialRecordStoreId} mainObjectMetadataItem={mainObjectMetadataItem}
mainObjectMetadataItem={mainObjectMetadataItem} fieldMetadataItem={fieldMetadataItem}
fieldMetadataItem={fieldMetadataItem} diffRecord={diffRecord}
diffRecord={diffRecord} />
/> <EventFieldDiffValue
<EventFieldDiffValue diffArtificialRecordStoreId={diffArtificialRecordStoreId}
diffArtificialRecordStoreId={diffArtificialRecordStoreId} mainObjectMetadataItem={mainObjectMetadataItem}
mainObjectMetadataItem={mainObjectMetadataItem} fieldMetadataItem={fieldMetadataItem}
fieldMetadataItem={fieldMetadataItem} />
/> </>
</> )}
)} </StyledEventFieldDiffContainer>
</StyledEventFieldDiffContainer>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -3,7 +3,6 @@ import { useSetRecoilState } from 'recoil';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@ -21,7 +20,6 @@ export const EventFieldDiffValueEffect = ({
const setEntity = useSetRecoilState( const setEntity = useSetRecoilState(
recordStoreFamilyState(diffArtificialRecordStoreId), recordStoreFamilyState(diffArtificialRecordStoreId),
); );
const setRecordValue = useSetRecordValue();
useEffect(() => { useEffect(() => {
if (!isDefined(diffRecord)) return; if (!isDefined(diffRecord)) return;
@ -33,14 +31,12 @@ export const EventFieldDiffValueEffect = ({
}; };
setEntity(forgedObjectRecord); setEntity(forgedObjectRecord);
setRecordValue(forgedObjectRecord.id, forgedObjectRecord);
}, [ }, [
diffRecord, diffRecord,
diffArtificialRecordStoreId, diffArtificialRecordStoreId,
fieldMetadataItem.name, fieldMetadataItem.name,
mainObjectMetadataItem.nameSingular, mainObjectMetadataItem.nameSingular,
setEntity, setEntity,
setRecordValue,
]); ]);
return <></>; return <></>;

View File

@ -4,10 +4,6 @@ import { FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE } from '@/activities/calend
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent'; import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -16,7 +12,6 @@ export const CommandMenuCalendarEventPage = () => {
const viewableRecordId = useRecoilComponentValueV2( const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState, viewableRecordIdComponentState,
); );
const setRecordValueInContextSelector = useSetRecordValue();
const { record: calendarEvent } = useFindOneRecord<CalendarEvent>({ const { record: calendarEvent } = useFindOneRecord<CalendarEvent>({
objectNameSingular: objectNameSingular:
@ -26,7 +21,6 @@ export const CommandMenuCalendarEventPage = () => {
// TODO: this is not executed on sub-sequent runs, make sure that it is intended // TODO: this is not executed on sub-sequent runs, make sure that it is intended
onCompleted: (record) => { onCompleted: (record) => {
upsertRecords([record]); upsertRecords([record]);
setRecordValueInContextSelector(record.id, record);
}, },
}); });
@ -35,9 +29,9 @@ export const CommandMenuCalendarEventPage = () => {
} }
return ( return (
<RecordFieldValueSelectorContextProvider> <>
<CalendarEventDetailsEffect record={calendarEvent} /> <CalendarEventDetailsEffect record={calendarEvent} />
<CalendarEventDetails calendarEvent={calendarEvent} /> <CalendarEventDetails calendarEvent={calendarEvent} />
</RecordFieldValueSelectorContextProvider> </>
); );
}; };

View File

@ -10,7 +10,6 @@ import { RecordShowContainer } from '@/object-record/record-show/components/Reco
import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect'; import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -72,24 +71,22 @@ export const CommandMenuRecordPage = () => {
value={{ instanceId: commandMenuPageInstanceId }} value={{ instanceId: commandMenuPageInstanceId }}
> >
<StyledRightDrawerRecord isMobile={isMobile}> <StyledRightDrawerRecord isMobile={isMobile}>
<RecordFieldValueSelectorContextProvider> <TimelineActivityContext.Provider
<TimelineActivityContext.Provider value={{
value={{ recordId: objectRecordId,
recordId: objectRecordId, }}
}} >
> <RecordShowEffect
<RecordShowEffect objectNameSingular={objectNameSingular}
objectNameSingular={objectNameSingular} recordId={objectRecordId}
recordId={objectRecordId} />
/> <RecordShowContainer
<RecordShowContainer objectNameSingular={objectNameSingular}
objectNameSingular={objectNameSingular} objectRecordId={objectRecordId}
objectRecordId={objectRecordId} loading={false}
loading={false} isInRightDrawer={true}
isInRightDrawer={true} />
/> </TimelineActivityContext.Provider>
</TimelineActivityContext.Provider>
</RecordFieldValueSelectorContextProvider>
</StyledRightDrawerRecord> </StyledRightDrawerRecord>
</ActionMenuComponentInstanceContext.Provider> </ActionMenuComponentInstanceContext.Provider>
</ContextStoreComponentInstanceContext.Provider> </ContextStoreComponentInstanceContext.Provider>

View File

@ -2,7 +2,6 @@ import { useContext } from 'react';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useSetRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue'; import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue';
@ -17,8 +16,6 @@ export const useClearField = () => {
const [updateRecord] = useUpdateRecord(); const [updateRecord] = useUpdateRecord();
const setRecordFieldValue = useSetRecordFieldValue();
const clearField = useRecoilCallback( const clearField = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
() => { () => {
@ -51,8 +48,6 @@ export const useClearField = () => {
emptyFieldValue, emptyFieldValue,
); );
setRecordFieldValue(recordId, fieldName, emptyFieldValue);
updateRecord?.({ updateRecord?.({
variables: { variables: {
where: { id: recordId }, where: { id: recordId },
@ -62,7 +57,7 @@ export const useClearField = () => {
}, },
}); });
}, },
[recordId, fieldDefinition, updateRecord, setRecordFieldValue], [recordId, fieldDefinition, updateRecord],
); );
return clearField; return clearField;

View File

@ -1,10 +1,10 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty'; import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty';
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { FieldContext } from '../contexts/FieldContext'; import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { FieldContext } from '../contexts/FieldContext';
export const useIsFieldEmpty = () => { export const useIsFieldEmpty = () => {
const { recordId, fieldDefinition, overridenIsFieldEmpty } = const { recordId, fieldDefinition, overridenIsFieldEmpty } =

View File

@ -1,102 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { RelationFromManyFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordFieldValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
import { ComponentDecorator } from 'twenty-ui/testing';
import {
fieldValue,
otherPersonMock,
relationFromManyFieldDisplayMock,
} from './relationFromManyFieldDisplayMock';
const RelationFieldValueSetterEffect = () => {
const setEntity = useSetRecoilState(
recordStoreFamilyState(relationFromManyFieldDisplayMock.recordId),
);
const setRelationEntity = useSetRecoilState(
recordStoreFamilyState(relationFromManyFieldDisplayMock.relationRecordId),
);
const setRecordFieldValue = useSetRecordFieldValue();
useEffect(() => {
setEntity(relationFromManyFieldDisplayMock.entityValue);
setRelationEntity(relationFromManyFieldDisplayMock.relationFieldValue);
setRecordFieldValue(
relationFromManyFieldDisplayMock.entityValue.id,
'company',
[relationFromManyFieldDisplayMock.entityValue],
);
setRecordFieldValue(otherPersonMock.entityValue.id, 'company', [
relationFromManyFieldDisplayMock.entityValue,
]);
setRecordFieldValue(
relationFromManyFieldDisplayMock.relationFieldValue.id,
'company',
relationFromManyFieldDisplayMock.relationFieldValue,
);
}, [setEntity, setRelationEntity, setRecordFieldValue]);
return null;
};
const meta: Meta = {
title: 'UI/Data/Field/Display/RelationFromManyFieldDisplay',
decorators: [
MemoryRouterDecorator,
ChipGeneratorsDecorator,
(Story) => (
<RecordFieldValueSelectorContextProvider>
<FieldContext.Provider
value={{
recordId: relationFromManyFieldDisplayMock.recordId,
isLabelIdentifier: false,
fieldDefinition: {
...relationFromManyFieldDisplayMock.fieldDefinition,
} as unknown as FieldDefinition<FieldMetadata>,
isReadOnly: false,
}}
>
<RelationFieldValueSetterEffect />
<Story />
</FieldContext.Provider>
</RecordFieldValueSelectorContextProvider>
),
ComponentDecorator,
],
component: RelationFromManyFieldDisplay,
argTypes: { value: { control: 'date' } },
args: { fieldValue: fieldValue },
parameters: {
chromatic: { disableSnapshot: true },
},
};
export default meta;
type Story = StoryObj<typeof RelationFromManyFieldDisplay>;
export const Default: Story = {};
// TODO: optimize this component once we have morph many
export const Performance = getProfilingStory({
componentName: 'RelationFromManyFieldDisplay',
averageThresholdInMs: 1,
numberOfRuns: 20,
numberOfTestsPerRun: 100,
});

View File

@ -1,13 +1,13 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useContext } from 'react';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/contexts/PreComputedChipGeneratorsContext'; import { PreComputedChipGeneratorsContext } from '@/object-metadata/contexts/PreComputedChipGeneratorsContext';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber'; import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; import { isNonEmptyString } from '@sniptt/guards';
import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { FieldContext } from '../../contexts/FieldContext'; import { FieldContext } from '../../contexts/FieldContext';
@ -38,7 +38,7 @@ export const useChipFieldDisplay = () => {
? fieldDefinition.metadata.objectMetadataNameSingular ? fieldDefinition.metadata.objectMetadataNameSingular
: undefined; : undefined;
const recordValue = useRecordValue(recordId); const recordValue = useRecoilValue(recordStoreFamilyState(recordId));
if (!isNonEmptyString(objectNameSingular)) { if (!isNonEmptyString(objectNameSingular)) {
throw new Error('Object metadata name singular is not a non-empty string'); throw new Error('Object metadata name singular is not a non-empty string');

View File

@ -11,7 +11,6 @@ import { recordIndexViewTypeState } from '@/object-record/record-index/states/re
import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper'; import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect'; import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect';
@ -48,48 +47,46 @@ export const RecordIndexContainer = () => {
<> <>
<StyledContainer> <StyledContainer>
<InformationBannerWrapper /> <InformationBannerWrapper />
<RecordFieldValueSelectorContextProvider> <SpreadsheetImportProvider>
<SpreadsheetImportProvider> <ViewBar
<ViewBar viewBarId={recordIndexId}
viewBarId={recordIndexId} optionsDropdownButton={
optionsDropdownButton={ <ObjectOptionsDropdown
<ObjectOptionsDropdown recordIndexId={recordIndexId}
recordIndexId={recordIndexId} objectMetadataItem={objectMetadataItem}
objectMetadataItem={objectMetadataItem} viewType={recordIndexViewType ?? ViewType.Table}
viewType={recordIndexViewType ?? ViewType.Table} />
/> }
} />
/> <RecordIndexViewBarEffect
<RecordIndexViewBarEffect objectNamePlural={objectNamePlural}
objectNamePlural={objectNamePlural} viewBarId={recordIndexId}
/>
</SpreadsheetImportProvider>
<RecordIndexFiltersToContextStoreEffect />
{recordIndexViewType === ViewType.Table && (
<>
<RecordIndexTableContainer
recordTableId={recordIndexId}
viewBarId={recordIndexId} viewBarId={recordIndexId}
/> />
</SpreadsheetImportProvider> <RecordIndexTableContainerEffect />
<RecordIndexFiltersToContextStoreEffect /> </>
{recordIndexViewType === ViewType.Table && ( )}
<> {recordIndexViewType === ViewType.Kanban && (
<RecordIndexTableContainer <StyledContainerWithPadding>
recordTableId={recordIndexId} <RecordIndexBoardContainer
viewBarId={recordIndexId} recordBoardId={recordIndexId}
/> viewBarId={recordIndexId}
<RecordIndexTableContainerEffect /> objectNameSingular={objectNameSingular}
</> />
)} <RecordIndexBoardDataLoader
{recordIndexViewType === ViewType.Kanban && ( objectNameSingular={objectNameSingular}
<StyledContainerWithPadding> recordBoardId={recordIndexId}
<RecordIndexBoardContainer />
recordBoardId={recordIndexId} <RecordIndexBoardDataLoaderEffect recordBoardId={recordIndexId} />
viewBarId={recordIndexId} </StyledContainerWithPadding>
objectNameSingular={objectNameSingular} )}
/>
<RecordIndexBoardDataLoader
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
<RecordIndexBoardDataLoaderEffect recordBoardId={recordIndexId} />
</StyledContainerWithPadding>
)}
</RecordFieldValueSelectorContextProvider>
</StyledContainer> </StyledContainer>
</> </>
); );

View File

@ -14,7 +14,7 @@ import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hook
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@ -32,7 +32,6 @@ export const useLoadRecordIndexBoardColumn = ({
recordBoardId, recordBoardId,
columnId, columnId,
}: UseLoadRecordIndexBoardProps) => { }: UseLoadRecordIndexBoardProps) => {
const setRecordValueInContextSelector = useSetRecordValue();
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular, objectNameSingular,
}); });
@ -102,10 +101,7 @@ export const useLoadRecordIndexBoardColumn = ({
useEffect(() => { useEffect(() => {
upsertRecordsInStore(records); upsertRecordsInStore(records);
for (const record of records) { }, [records, upsertRecordsInStore]);
setRecordValueInContextSelector(record.id, record);
}
}, [records, upsertRecordsInStore, setRecordValueInContextSelector]);
return { return {
records, records,

View File

@ -2,7 +2,6 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { buildFindOneRecordForShowPageOperationSignature } from '@/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory'; import { buildFindOneRecordForShowPageOperationSignature } from '@/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useEffect } from 'react'; import { useEffect } from 'react';
@ -19,7 +18,6 @@ export const RecordShowEffect = ({
}: RecordShowEffectProps) => { }: RecordShowEffectProps) => {
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
const { objectMetadataItems } = useObjectMetadataItems(); const { objectMetadataItems } = useObjectMetadataItems();
const setRecordValueInContextSelector = useSetRecordValue();
const FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE = const FIND_ONE_RECORD_FOR_SHOW_PAGE_OPERATION_SIGNATURE =
buildFindOneRecordForShowPageOperationSignature({ buildFindOneRecordForShowPageOperationSignature({
@ -44,10 +42,8 @@ export const RecordShowEffect = ({
if (JSON.stringify(previousRecordValue) !== JSON.stringify(newRecord)) { if (JSON.stringify(previousRecordValue) !== JSON.stringify(newRecord)) {
set(recordStoreFamilyState(recordId), newRecord); set(recordStoreFamilyState(recordId), newRecord);
} }
setRecordValueInContextSelector(recordId, newRecord);
}, },
[recordId, setRecordValueInContextSelector], [recordId],
); );
useEffect(() => { useEffect(() => {

View File

@ -3,7 +3,7 @@ import { useContext, useEffect } from 'react';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@ -14,8 +14,6 @@ type RecordDetailRelationRecordsListItemEffectProps = {
export const RecordDetailRelationRecordsListItemEffect = ({ export const RecordDetailRelationRecordsListItemEffect = ({
relationRecordId, relationRecordId,
}: RecordDetailRelationRecordsListItemEffectProps) => { }: RecordDetailRelationRecordsListItemEffectProps) => {
const setRecordValueInContextSelector = useSetRecordValue();
const { fieldDefinition } = useContext(FieldContext); const { fieldDefinition } = useContext(FieldContext);
const { relationObjectMetadataNameSingular } = const { relationObjectMetadataNameSingular } =
@ -31,9 +29,8 @@ export const RecordDetailRelationRecordsListItemEffect = ({
useEffect(() => { useEffect(() => {
if (isDefined(record)) { if (isDefined(record)) {
upsertRecords([record]); upsertRecords([record]);
setRecordValueInContextSelector(record.id, record);
} }
}, [record, upsertRecords, setRecordValueInContextSelector]); }, [record, upsertRecords]);
return null; return null;
}; };

View File

@ -1,78 +1,16 @@
import { Dispatch, SetStateAction, useState } from 'react'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { createContext, useContextSelector } from 'use-context-selector'; import { useRecoilValue } from 'recoil';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export type RecordFieldValue = {
[recordId: string]: {
[fieldName: string]: any;
};
};
export const RecordFieldValueSelectorContext = createContext<
[RecordFieldValue, Dispatch<SetStateAction<RecordFieldValue>>]
>([{}, () => {}]);
export const useSetRecordValue = () => {
const setTableValue = useContextSelector(
RecordFieldValueSelectorContext,
(value) => value[1],
);
return (recordId: string, newRecord: any) => {
setTableValue((currentTable) => ({
...currentTable,
[recordId]: newRecord,
}));
};
};
export const useRecordValue = (recordId: string) => {
const tableValue = useContextSelector(
RecordFieldValueSelectorContext,
(value) => value[0]?.[recordId],
);
return tableValue as ObjectRecord | undefined;
};
export const useRecordFieldValue = <T,>( export const useRecordFieldValue = <T,>(
recordId: string, recordId: string,
fieldName: string, fieldName: string,
) => { ) => {
const recordFieldValue = useContextSelector( const recordFieldValue = useRecoilValue(
RecordFieldValueSelectorContext, recordStoreFamilySelector({
(value) => value[0]?.[recordId]?.[fieldName], recordId,
fieldName,
}),
); );
return recordFieldValue as T | undefined; return recordFieldValue as T | undefined;
}; };
export const useSetRecordFieldValue = () => {
const setTableValue = useContextSelector(
RecordFieldValueSelectorContext,
(value) => value[1],
);
return (recordId: string, fieldName: string, newValue: any) => {
setTableValue((currentTable) => ({
...currentTable,
[recordId]: {
...currentTable[recordId],
[fieldName]: newValue,
},
}));
};
};
export const RecordFieldValueSelectorContextProvider = ({
children,
}: {
children: any;
}) => (
<RecordFieldValueSelectorContext.Provider
value={useState<RecordFieldValue>({})}
>
{children}
</RecordFieldValueSelectorContext.Provider>
);

View File

@ -6,10 +6,6 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage'; import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
@ -36,22 +32,14 @@ const RelationFieldValueSetterEffect = () => {
recordStoreFamilyState(mockPerformance.relationRecordId), recordStoreFamilyState(mockPerformance.relationRecordId),
); );
const setRecordValue = useSetRecordValue();
const [, setObjectMetadataItems] = useRecoilState(objectMetadataItemsState); const [, setObjectMetadataItems] = useRecoilState(objectMetadataItemsState);
useEffect(() => { useEffect(() => {
setEntity(mockPerformance.entityValue); setEntity(mockPerformance.entityValue);
setRelationEntity(mockPerformance.relationFieldValue); setRelationEntity(mockPerformance.relationFieldValue);
setRecordValue(mockPerformance.entityValue.id, mockPerformance.entityValue);
setRecordValue(
mockPerformance.relationFieldValue.id,
mockPerformance.relationFieldValue,
);
setObjectMetadataItems(generatedMockObjectMetadataItems); setObjectMetadataItems(generatedMockObjectMetadataItems);
}, [setEntity, setRelationEntity, setRecordValue, setObjectMetadataItems]); }, [setEntity, setRelationEntity, setObjectMetadataItems]);
return null; return null;
}; };
@ -63,95 +51,93 @@ const meta: Meta = {
ChipGeneratorsDecorator, ChipGeneratorsDecorator,
(Story) => { (Story) => {
return ( return (
<RecordFieldValueSelectorContextProvider> <RecordIndexContextProvider
<RecordIndexContextProvider value={{
indexIdentifierUrl: (_recordId: string) => '',
onIndexRecordsLoaded: () => {},
objectNamePlural: 'companies',
objectNameSingular: 'company',
objectMetadataItem: mockPerformance.objectMetadataItem as any,
recordIndexId: 'recordIndexId',
}}
>
<RecordTableContextProvider
value={{ value={{
indexIdentifierUrl: (_recordId: string) => '', recordTableId: 'recordTableId',
onIndexRecordsLoaded: () => {}, viewBarId: mockPerformance.recordId,
objectNamePlural: 'companies',
objectNameSingular: 'company',
objectMetadataItem: mockPerformance.objectMetadataItem as any, objectMetadataItem: mockPerformance.objectMetadataItem as any,
recordIndexId: 'recordIndexId', visibleTableColumns: mockPerformance.visibleTableColumns as any,
objectNameSingular:
mockPerformance.objectMetadataItem.nameSingular,
}} }}
> >
<RecordTableContextProvider <RecordTableComponentInstance
value={{ recordTableId="asd"
recordTableId: 'recordTableId', onColumnsChange={() => {}}
viewBarId: mockPerformance.recordId,
objectMetadataItem: mockPerformance.objectMetadataItem as any,
visibleTableColumns: mockPerformance.visibleTableColumns as any,
objectNameSingular:
mockPerformance.objectMetadataItem.nameSingular,
}}
> >
<RecordTableComponentInstance <RecordTableBodyContextProvider
recordTableId="asd" value={{
onColumnsChange={() => {}} onOpenTableCell: () => {},
onMoveFocus: () => {},
onCloseTableCell: () => {},
onMoveHoverToCurrentCell: () => {},
onActionMenuDropdownOpened: () => {},
onCellMouseEnter: () => {},
}}
> >
<RecordTableBodyContextProvider <RecordTableRowContextProvider
value={{ value={{
onOpenTableCell: () => {}, objectNameSingular:
onMoveFocus: () => {}, mockPerformance.entityValue.__typename.toLocaleLowerCase(),
onCloseTableCell: () => {}, recordId: mockPerformance.recordId,
onMoveHoverToCurrentCell: () => {}, rowIndex: 0,
onActionMenuDropdownOpened: () => {}, pathToShowPage:
onCellMouseEnter: () => {}, getBasePathToShowPage({
objectNameSingular:
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
}) + mockPerformance.recordId,
isSelected: false,
inView: true,
}} }}
> >
<RecordTableRowContextProvider <RecordTableRowDraggableContextProvider
value={{ value={{
objectNameSingular: isDragging: false,
mockPerformance.entityValue.__typename.toLocaleLowerCase(), dragHandleProps: null,
recordId: mockPerformance.recordId,
rowIndex: 0,
pathToShowPage:
getBasePathToShowPage({
objectNameSingular:
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
}) + mockPerformance.recordId,
isSelected: false,
inView: true,
}} }}
> >
<RecordTableRowDraggableContextProvider <RecordTableCellContext.Provider
value={{ value={{
isDragging: false, columnDefinition: mockPerformance.fieldDefinition,
dragHandleProps: null, cellPosition: { row: 0, column: 0 },
}} }}
> >
<RecordTableCellContext.Provider <FieldContext.Provider
value={{ value={{
columnDefinition: mockPerformance.fieldDefinition, recordId: mockPerformance.recordId,
cellPosition: { row: 0, column: 0 }, isLabelIdentifier: false,
fieldDefinition: {
...mockPerformance.fieldDefinition,
},
isReadOnly: false,
}} }}
> >
<FieldContext.Provider <RelationFieldValueSetterEffect />
value={{ <table>
recordId: mockPerformance.recordId, <tbody>
isLabelIdentifier: false, <tr>
fieldDefinition: { <Story />
...mockPerformance.fieldDefinition, </tr>
}, </tbody>
isReadOnly: false, </table>
}} </FieldContext.Provider>
> </RecordTableCellContext.Provider>
<RelationFieldValueSetterEffect /> </RecordTableRowDraggableContextProvider>
<table> </RecordTableRowContextProvider>
<tbody> </RecordTableBodyContextProvider>
<tr> </RecordTableComponentInstance>
<Story /> </RecordTableContextProvider>
</tr> </RecordIndexContextProvider>
</tbody>
</table>
</FieldContext.Provider>
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
</RecordTableBodyContextProvider>
</RecordTableComponentInstance>
</RecordTableContextProvider>
</RecordIndexContextProvider>
</RecordFieldValueSelectorContextProvider>
); );
}, },
ComponentDecorator, ComponentDecorator,

View File

@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
import { useSetRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive'; import { useSetIsRecordTableFocusActive } from '@/object-record/record-table/record-table-cell/hooks/useSetIsRecordTableFocusActive';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
@ -27,8 +27,6 @@ export const useSetRecordTableData = ({
recordTableId, recordTableId,
onEntityCountChange, onEntityCountChange,
}: useSetRecordTableDataProps) => { }: useSetRecordTableDataProps) => {
const setRecordValueInContextSelector = useSetRecordValue();
const recordIndexRecordIdsByGroupFamilyState = const recordIndexRecordIdsByGroupFamilyState =
useRecoilComponentCallbackStateV2( useRecoilComponentCallbackStateV2(
recordIndexRecordIdsByGroupComponentFamilyState, recordIndexRecordIdsByGroupComponentFamilyState,
@ -82,7 +80,6 @@ export const useSetRecordTableData = ({
}; };
set(recordStoreFamilyState(record.id), newRecord); set(recordStoreFamilyState(record.id), newRecord);
setRecordValueInContextSelector(record.id, newRecord);
} }
} }
@ -130,7 +127,6 @@ export const useSetRecordTableData = ({
setRecordTableHoverPosition, setRecordTableHoverPosition,
onEntityCountChange, onEntityCountChange,
isRowSelectedFamilyState, isRowSelectedFamilyState,
setRecordValueInContextSelector,
], ],
); );
}; };

View File

@ -1,12 +1,13 @@
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey'; import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { TitleInputHotkeyScope } from '@/ui/input/types/TitleInputHotkeyScope'; import { TitleInputHotkeyScope } from '@/ui/input/types/TitleInputHotkeyScope';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { Theme, withTheme } from '@emotion/react'; import { Theme, withTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { OverflowingTextWithTooltip } from 'twenty-ui/display'; import { OverflowingTextWithTooltip } from 'twenty-ui/display';
const StyledDiv = styled.div` const StyledDiv = styled.div`
@ -33,7 +34,9 @@ const StyledEmptyText = withTheme(styled.div<{ theme: Theme }>`
export const RecordTitleCellSingleTextDisplayMode = () => { export const RecordTitleCellSingleTextDisplayMode = () => {
const { recordId, fieldDefinition } = useContext(FieldContext); const { recordId, fieldDefinition } = useContext(FieldContext);
const recordValue = useRecordValue(recordId);
const recordValue = useRecoilValue(recordStoreFamilyState(recordId));
const isEmpty = const isEmpty =
recordValue?.[fieldDefinition.metadata.fieldName]?.trim() === ''; recordValue?.[fieldDefinition.metadata.fieldName]?.trim() === '';

View File

@ -112,10 +112,7 @@ export const SettingsDataModelFieldPreview = ({
}} }}
> >
{isDefined(previewRecord) ? ( {isDefined(previewRecord) ? (
<SettingsDataModelSetPreviewRecordEffect <SettingsDataModelSetPreviewRecordEffect record={previewRecord} />
fieldName={fieldName}
record={previewRecord}
/>
) : ( ) : (
<SettingsDataModelSetFieldValueEffect <SettingsDataModelSetFieldValueEffect
recordId={recordId} recordId={recordId}

View File

@ -1,4 +1,3 @@
import { useSetRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { settingsPreviewRecordIdState } from '@/settings/data-model/fields/preview/states/settingsPreviewRecordIdState'; import { settingsPreviewRecordIdState } from '@/settings/data-model/fields/preview/states/settingsPreviewRecordIdState';
@ -29,7 +28,6 @@ export const SettingsDataModelSetFieldValueEffect = ({
fieldName, fieldName,
}), }),
); );
const setRecordFieldValue = useSetRecordFieldValue();
useEffect(() => { useEffect(() => {
if ( if (
@ -37,23 +35,10 @@ export const SettingsDataModelSetFieldValueEffect = ({
!!upsertedPreviewRecord[fieldName] !!upsertedPreviewRecord[fieldName]
) { ) {
setFieldValue(upsertedPreviewRecord[fieldName]); setFieldValue(upsertedPreviewRecord[fieldName]);
setRecordFieldValue(
recordId,
fieldName,
upsertedPreviewRecord[fieldName],
);
} else { } else {
setFieldValue(value); setFieldValue(value);
setRecordFieldValue(recordId, fieldName, value);
} }
}, [ }, [value, setFieldValue, recordId, fieldName, upsertedPreviewRecord]);
value,
setFieldValue,
setRecordFieldValue,
recordId,
fieldName,
upsertedPreviewRecord,
]);
return null; return null;
}; };

View File

@ -1,4 +1,3 @@
import { useSetRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { settingsPreviewRecordIdState } from '@/settings/data-model/fields/preview/states/settingsPreviewRecordIdState'; import { settingsPreviewRecordIdState } from '@/settings/data-model/fields/preview/states/settingsPreviewRecordIdState';
@ -7,15 +6,12 @@ import { useSetRecoilState } from 'recoil';
type SettingsDataModelSetPreviewRecordEffectProps = { type SettingsDataModelSetPreviewRecordEffectProps = {
record: ObjectRecord; record: ObjectRecord;
fieldName: string;
}; };
export const SettingsDataModelSetPreviewRecordEffect = ({ export const SettingsDataModelSetPreviewRecordEffect = ({
record, record,
fieldName,
}: SettingsDataModelSetPreviewRecordEffectProps) => { }: SettingsDataModelSetPreviewRecordEffectProps) => {
const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore();
const setRecordFieldValue = useSetRecordFieldValue();
const setSettingsPreviewRecordId = useSetRecoilState( const setSettingsPreviewRecordId = useSetRecoilState(
settingsPreviewRecordIdState, settingsPreviewRecordIdState,
@ -23,15 +19,8 @@ export const SettingsDataModelSetPreviewRecordEffect = ({
useEffect(() => { useEffect(() => {
upsertRecordsInStore([record]); upsertRecordsInStore([record]);
setRecordFieldValue(record.id, fieldName, record[fieldName]);
setSettingsPreviewRecordId(record.id); setSettingsPreviewRecordId(record.id);
}, [ }, [record, upsertRecordsInStore, setSettingsPreviewRecordId]);
record,
upsertRecordsInStore,
setRecordFieldValue,
fieldName,
setSettingsPreviewRecordId,
]);
return null; return null;
}; };

View File

@ -1,16 +1,15 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem'; import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SettingsUpdateDataModelObjectAboutForm } from '@/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm'; import { SettingsUpdateDataModelObjectAboutForm } from '@/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm';
import { SettingsDataModelObjectSettingsFormCard } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectSettingsFormCard'; import { SettingsDataModelObjectSettingsFormCard } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectSettingsFormCard';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { Button } from 'twenty-ui/input';
import { H2Title, IconArchive } from 'twenty-ui/display'; import { H2Title, IconArchive } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout'; import { Section } from 'twenty-ui/layout';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
type ObjectSettingsProps = { type ObjectSettingsProps = {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
@ -39,43 +38,38 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => {
}; };
return ( return (
<RecordFieldValueSelectorContextProvider> <StyledContentContainer>
<StyledContentContainer> <StyledFormSection>
<StyledFormSection> <H2Title
title={t`About`}
description={t`Name in both singular (e.g., 'Invoice') and plural (e.g., 'Invoices') forms.`}
/>
<SettingsUpdateDataModelObjectAboutForm
objectMetadataItem={objectMetadataItem}
/>
</StyledFormSection>
<StyledFormSection>
<Section>
<H2Title <H2Title
title={t`About`} title={t`Options`}
description={t`Name in both singular (e.g., 'Invoice') and plural (e.g., 'Invoices') forms.`} description={t`Choose the fields that will identify your records`}
/> />
<SettingsUpdateDataModelObjectAboutForm <SettingsDataModelObjectSettingsFormCard
objectMetadataItem={objectMetadataItem} objectMetadataItem={objectMetadataItem}
/> />
</StyledFormSection> </Section>
<StyledFormSection> </StyledFormSection>
<Section> <StyledFormSection>
<H2Title <Section>
title={t`Options`} <H2Title title={t`Danger zone`} description={t`Deactivate object`} />
description={t`Choose the fields that will identify your records`} <Button
/> Icon={IconArchive}
<SettingsDataModelObjectSettingsFormCard title={t`Deactivate`}
objectMetadataItem={objectMetadataItem} size="small"
/> onClick={handleDisable}
</Section> />
</StyledFormSection> </Section>
<StyledFormSection> </StyledFormSection>
<Section> </StyledContentContainer>
<H2Title
title={t`Danger zone`}
description={t`Deactivate object`}
/>
<Button
Icon={IconArchive}
title={t`Deactivate`}
size="small"
onClick={handleDisable}
/>
</Section>
</StyledFormSection>
</StyledContentContainer>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -1,6 +1,5 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SignInBackgroundMockContainer } from '@/sign-in-background-mock/components/SignInBackgroundMockContainer'; import { SignInBackgroundMockContainer } from '@/sign-in-background-mock/components/SignInBackgroundMockContainer';
import { PageBody } from '@/ui/layout/page/components/PageBody'; import { PageBody } from '@/ui/layout/page/components/PageBody';
import { PageContainer } from '@/ui/layout/page/components/PageContainer'; import { PageContainer } from '@/ui/layout/page/components/PageContainer';
@ -18,11 +17,9 @@ export const SignInBackgroundMockPage = () => {
<PageContainer> <PageContainer>
<PageHeader title="Companies" Icon={IconBuildingSkyscraper} /> <PageHeader title="Companies" Icon={IconBuildingSkyscraper} />
<PageBody> <PageBody>
<RecordFieldValueSelectorContextProvider> <StyledTableContainer>
<StyledTableContainer> <SignInBackgroundMockContainer />
<SignInBackgroundMockContainer /> </StyledTableContainer>
</StyledTableContainer>
</RecordFieldValueSelectorContextProvider>
</PageBody> </PageBody>
</PageContainer> </PageContainer>
); );

View File

@ -11,7 +11,6 @@ import { RecordShowContainer } from '@/object-record/record-show/components/Reco
import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect'; import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { PageHeaderToggleCommandMenuButton } from '@/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton'; import { PageHeaderToggleCommandMenuButton } from '@/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton';
import { PageBody } from '@/ui/layout/page/components/PageBody'; import { PageBody } from '@/ui/layout/page/components/PageBody';
import { PageContainer } from '@/ui/layout/page/components/PageContainer'; import { PageContainer } from '@/ui/layout/page/components/PageContainer';
@ -30,57 +29,55 @@ export const RecordShowPage = () => {
); );
return ( return (
<RecordFieldValueSelectorContextProvider> <RecordFilterGroupsComponentInstanceContext.Provider
<RecordFilterGroupsComponentInstanceContext.Provider value={{ instanceId: `record-show-${objectRecordId}` }}
>
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: `record-show-${objectRecordId}` }} value={{ instanceId: `record-show-${objectRecordId}` }}
> >
<RecordFiltersComponentInstanceContext.Provider <RecordSortsComponentInstanceContext.Provider
value={{ instanceId: `record-show-${objectRecordId}` }} value={{ instanceId: `record-show-${objectRecordId}` }}
> >
<RecordSortsComponentInstanceContext.Provider <ContextStoreComponentInstanceContext.Provider
value={{ instanceId: `record-show-${objectRecordId}` }} value={{ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID }}
> >
<ContextStoreComponentInstanceContext.Provider <ActionMenuComponentInstanceContext.Provider
value={{ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID }} value={{ instanceId: `record-show-${objectRecordId}` }}
> >
<ActionMenuComponentInstanceContext.Provider <PageContainer>
value={{ instanceId: `record-show-${objectRecordId}` }} <RecordShowPageTitle
> objectNameSingular={objectNameSingular}
<PageContainer> objectRecordId={objectRecordId}
<RecordShowPageTitle />
objectNameSingular={objectNameSingular} <RecordShowPageHeader
objectRecordId={objectRecordId} objectNameSingular={objectNameSingular}
/> objectRecordId={objectRecordId}
<RecordShowPageHeader >
objectNameSingular={objectNameSingular} <RecordShowActionMenu />
objectRecordId={objectRecordId} <PageHeaderToggleCommandMenuButton />
</RecordShowPageHeader>
<PageBody>
<TimelineActivityContext.Provider
value={{
recordId: objectRecordId,
}}
> >
<RecordShowActionMenu /> <RecordShowEffect
<PageHeaderToggleCommandMenuButton /> objectNameSingular={objectNameSingular}
</RecordShowPageHeader> recordId={objectRecordId}
<PageBody> />
<TimelineActivityContext.Provider <RecordShowContainer
value={{ objectNameSingular={objectNameSingular}
recordId: objectRecordId, objectRecordId={objectRecordId}
}} loading={false}
> />
<RecordShowEffect </TimelineActivityContext.Provider>
objectNameSingular={objectNameSingular} </PageBody>
recordId={objectRecordId} </PageContainer>
/> </ActionMenuComponentInstanceContext.Provider>
<RecordShowContainer </ContextStoreComponentInstanceContext.Provider>
objectNameSingular={objectNameSingular} </RecordSortsComponentInstanceContext.Provider>
objectRecordId={objectRecordId} </RecordFiltersComponentInstanceContext.Provider>
loading={false} </RecordFilterGroupsComponentInstanceContext.Provider>
/>
</TimelineActivityContext.Provider>
</PageBody>
</PageContainer>
</ActionMenuComponentInstanceContext.Provider>
</ContextStoreComponentInstanceContext.Provider>
</RecordSortsComponentInstanceContext.Provider>
</RecordFiltersComponentInstanceContext.Provider>
</RecordFilterGroupsComponentInstanceContext.Provider>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -12,7 +12,6 @@ import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMe
import { useUpdateOneFieldMetadataItem } from '@/object-metadata/hooks/useUpdateOneFieldMetadataItem'; import { useUpdateOneFieldMetadataItem } from '@/object-metadata/hooks/useUpdateOneFieldMetadataItem';
import { formatFieldMetadataItemInput } from '@/object-metadata/utils/formatFieldMetadataItemInput'; import { formatFieldMetadataItemInput } from '@/object-metadata/utils/formatFieldMetadataItemInput';
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField'; import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength'; import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength';
@ -161,7 +160,7 @@ export const SettingsObjectFieldEdit = () => {
}; };
return ( return (
<RecordFieldValueSelectorContextProvider> <>
{/* eslint-disable-next-line react/jsx-props-no-spreading */} {/* eslint-disable-next-line react/jsx-props-no-spreading */}
<FormProvider {...formConfig}> <FormProvider {...formConfig}>
<SubMenuTopBarContainer <SubMenuTopBarContainer
@ -265,6 +264,6 @@ export const SettingsObjectFieldEdit = () => {
</SettingsPageContainer> </SettingsPageContainer>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>
</FormProvider> </FormProvider>
</RecordFieldValueSelectorContextProvider> </>
); );
}; };

View File

@ -3,7 +3,6 @@ import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataIt
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown'; import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown';
@ -27,6 +26,8 @@ import pick from 'lodash.pick';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { useParams, useSearchParams } from 'react-router-dom'; import { useParams, useSearchParams } from 'react-router-dom';
import { H2Title } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
import { z } from 'zod'; import { z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp'; import { useNavigateApp } from '~/hooks/useNavigateApp';
@ -35,8 +36,6 @@ import { DEFAULT_ICONS_BY_FIELD_TYPE } from '~/pages/settings/data-model/constan
import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils'; import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { H2Title } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
type SettingsDataModelNewFieldFormValues = z.infer< type SettingsDataModelNewFieldFormValues = z.infer<
ReturnType<typeof settingsFieldFormSchema> ReturnType<typeof settingsFieldFormSchema>
@ -200,87 +199,85 @@ export const SettingsObjectNewFieldConfigure = () => {
if (!activeObjectMetadataItem) return null; if (!activeObjectMetadataItem) return null;
return ( return (
<RecordFieldValueSelectorContextProvider> <FormProvider // eslint-disable-next-line react/jsx-props-no-spreading
<FormProvider // eslint-disable-next-line react/jsx-props-no-spreading {...formConfig}
{...formConfig} >
> <SubMenuTopBarContainer
<SubMenuTopBarContainer title={t`2. Configure field`}
title={t`2. Configure field`} links={[
links={[ {
{ children: t`Workspace`,
children: t`Workspace`, href: getSettingsPath(SettingsPath.Workspace),
href: getSettingsPath(SettingsPath.Workspace), },
}, {
{ children: t`Objects`,
children: t`Objects`, href: getSettingsPath(SettingsPath.Objects),
href: getSettingsPath(SettingsPath.Objects), },
}, {
{ children: activeObjectMetadataItem.labelPlural,
children: activeObjectMetadataItem.labelPlural, href: getSettingsPath(SettingsPath.ObjectDetail, {
href: getSettingsPath(SettingsPath.ObjectDetail, { objectNamePlural,
objectNamePlural, }),
}), },
},
{ children: <SettingsDataModelNewFieldBreadcrumbDropDown /> }, { children: <SettingsDataModelNewFieldBreadcrumbDropDown /> },
]} ]}
actionButton={ actionButton={
<SaveAndCancelButtons <SaveAndCancelButtons
isSaveDisabled={!canSave} isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting} isCancelDisabled={isSubmitting}
onCancel={() => onCancel={() =>
navigate( navigate(
SettingsPath.ObjectNewFieldSelect, SettingsPath.ObjectNewFieldSelect,
{ {
objectNamePlural, objectNamePlural,
}, },
{ {
fieldType, fieldType,
}, },
) )
} }
onSave={formConfig.handleSubmit(handleSave)} onSave={formConfig.handleSubmit(handleSave)}
/>
}
>
<SettingsPageContainer>
<Section>
<H2Title
title={t`Icon and Name`}
description={t`The name and icon of this field`}
/> />
} <SettingsDataModelFieldIconLabelForm
> maxLength={FIELD_NAME_MAXIMUM_LENGTH}
<SettingsPageContainer> canToggleSyncLabelWithName={
<Section> fieldType !== FieldMetadataType.RELATION
<H2Title }
title={t`Icon and Name`} />
description={t`The name and icon of this field`} </Section>
/> <Section>
<SettingsDataModelFieldIconLabelForm <H2Title
maxLength={FIELD_NAME_MAXIMUM_LENGTH} title={t`Values`}
canToggleSyncLabelWithName={ description={t`The values of this field`}
fieldType !== FieldMetadataType.RELATION />
} <SettingsDataModelFieldSettingsFormCard
/> fieldMetadataItem={{
</Section> icon: formConfig.watch('icon'),
<Section> label: formConfig.watch('label') || 'New Field',
<H2Title settings: formConfig.watch('settings') || null,
title={t`Values`} type: fieldType as FieldMetadataType,
description={t`The values of this field`} }}
/> objectMetadataItem={activeObjectMetadataItem}
<SettingsDataModelFieldSettingsFormCard />
fieldMetadataItem={{ </Section>
icon: formConfig.watch('icon'), <Section>
label: formConfig.watch('label') || 'New Field', <H2Title
settings: formConfig.watch('settings') || null, title={t`Description`}
type: fieldType as FieldMetadataType, description={t`The description of this field`}
}} />
objectMetadataItem={activeObjectMetadataItem} <SettingsDataModelFieldDescriptionForm />
/> </Section>
</Section> </SettingsPageContainer>
<Section> </SubMenuTopBarContainer>
<H2Title </FormProvider>
title={t`Description`}
description={t`The description of this field`}
/>
<SettingsDataModelFieldDescriptionForm />
</Section>
</SettingsPageContainer>
</SubMenuTopBarContainer>
</FormProvider>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -1,5 +1,4 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown'; import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown';
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs'; import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
@ -10,15 +9,15 @@ import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { t } from '@lingui/core/macro';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { isDefined } from 'twenty-shared/utils';
import { z } from 'zod'; import { z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp'; import { useNavigateApp } from '~/hooks/useNavigateApp';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
export const settingsDataModelFieldTypeFormSchema = z.object({ export const settingsDataModelFieldTypeFormSchema = z.object({
type: z.enum( type: z.enum(
@ -64,32 +63,30 @@ export const SettingsObjectNewFieldSelect = () => {
if (!activeObjectMetadataItem) return null; if (!activeObjectMetadataItem) return null;
return ( return (
<RecordFieldValueSelectorContextProvider> <FormProvider // eslint-disable-next-line react/jsx-props-no-spreading
<FormProvider // eslint-disable-next-line react/jsx-props-no-spreading {...formMethods}
{...formMethods} >
<SubMenuTopBarContainer
title={t`1. Select a field type`}
links={[
{ children: t`Workspace`, href: '/settings/workspace' },
{ children: t`Objects`, href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: getSettingsPath(SettingsPath.ObjectDetail, {
objectNamePlural,
}),
},
{ children: <SettingsDataModelNewFieldBreadcrumbDropDown /> },
]}
> >
<SubMenuTopBarContainer <SettingsPageContainer>
title={t`1. Select a field type`} <SettingsObjectNewFieldSelector
links={[ objectNamePlural={objectNamePlural}
{ children: t`Workspace`, href: '/settings/workspace' }, excludedFieldTypes={excludedFieldTypes}
{ children: t`Objects`, href: '/settings/objects' }, />
{ </SettingsPageContainer>
children: activeObjectMetadataItem.labelPlural, </SubMenuTopBarContainer>
href: getSettingsPath(SettingsPath.ObjectDetail, { </FormProvider>
objectNamePlural,
}),
},
{ children: <SettingsDataModelNewFieldBreadcrumbDropDown /> },
]}
>
<SettingsPageContainer>
<SettingsObjectNewFieldSelector
objectNamePlural={objectNamePlural}
excludedFieldTypes={excludedFieldTypes}
/>
</SettingsPageContainer>
</SubMenuTopBarContainer>
</FormProvider>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -10,7 +10,6 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext'; import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates'; import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext'; import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext'; import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
@ -126,41 +125,39 @@ export const RecordTableDecorator: Decorator = (Story, context) => {
); );
return ( return (
<RecordFieldValueSelectorContextProvider> <RecordTableComponentInstanceContext.Provider
<RecordTableComponentInstanceContext.Provider value={{ instanceId: recordIndexId, onColumnsChange: () => {} }}
value={{ instanceId: recordIndexId, onColumnsChange: () => {} }} >
<ViewComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
> >
<ViewComponentInstanceContext.Provider <RecordFilterGroupsComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }} value={{ instanceId: recordIndexId }}
> >
<RecordFilterGroupsComponentInstanceContext.Provider <RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }} value={{ instanceId: recordIndexId }}
> >
<RecordFiltersComponentInstanceContext.Provider <RecordSortsComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }} value={{ instanceId: recordIndexId }}
> >
<RecordSortsComponentInstanceContext.Provider <ActionMenuComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }} value={{
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
}}
> >
<ActionMenuComponentInstanceContext.Provider <InternalTableStateLoaderEffect
value={{ objectMetadataItem={objectMetadataItem}
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId), />
}} <InternalTableContextProviders
objectMetadataItem={objectMetadataItem}
> >
<InternalTableStateLoaderEffect <Story />
objectMetadataItem={objectMetadataItem} </InternalTableContextProviders>
/> </ActionMenuComponentInstanceContext.Provider>
<InternalTableContextProviders </RecordSortsComponentInstanceContext.Provider>
objectMetadataItem={objectMetadataItem} </RecordFiltersComponentInstanceContext.Provider>
> </RecordFilterGroupsComponentInstanceContext.Provider>
<Story /> </ViewComponentInstanceContext.Provider>
</InternalTableContextProviders> </RecordTableComponentInstanceContext.Provider>
</ActionMenuComponentInstanceContext.Provider>
</RecordSortsComponentInstanceContext.Provider>
</RecordFiltersComponentInstanceContext.Provider>
</RecordFilterGroupsComponentInstanceContext.Provider>
</ViewComponentInstanceContext.Provider>
</RecordTableComponentInstanceContext.Provider>
</RecordFieldValueSelectorContextProvider>
); );
}; };

View File

@ -6,10 +6,6 @@ import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/uti
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField'; import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@ -27,15 +23,12 @@ const RecordMockSetterEffect = ({
people: ObjectRecord[]; people: ObjectRecord[];
tasks: ObjectRecord[]; tasks: ObjectRecord[];
}) => { }) => {
const setRecordValue = useSetRecordValue();
const setRecordInStores = useRecoilCallback( const setRecordInStores = useRecoilCallback(
({ set }) => ({ set }) =>
(record: ObjectRecord) => { (record: ObjectRecord) => {
set(recordStoreFamilyState(record.id), record); set(recordStoreFamilyState(record.id), record);
setRecordValue(record.id, record);
}, },
[setRecordValue], [],
); );
useEffect(() => { useEffect(() => {
@ -130,27 +123,25 @@ export const getFieldDecorator =
instanceId: 'record-field-component-instance-id', instanceId: 'record-field-component-instance-id',
}} }}
> >
<RecordFieldValueSelectorContextProvider> <FieldContext.Provider
<FieldContext.Provider value={{
value={{ recordId: record.id,
recordId: record.id, isLabelIdentifier,
isLabelIdentifier, fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ field: fieldMetadataItem,
field: fieldMetadataItem, position: 0,
position: 0, objectMetadataItem,
objectMetadataItem, }),
}), isReadOnly: false,
isReadOnly: false, }}
}} >
> <RecordMockSetterEffect
<RecordMockSetterEffect companies={companies}
companies={companies} people={people}
people={people} tasks={tasks}
tasks={tasks} />
/> <Story />
<Story /> </FieldContext.Provider>
</FieldContext.Provider>
</RecordFieldValueSelectorContextProvider>
</RecordFieldComponentInstanceContext.Provider> </RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -55579,7 +55579,6 @@ __metadata:
tsx: "npm:^4.17.0" tsx: "npm:^4.17.0"
type-fest: "npm:4.10.1" type-fest: "npm:4.10.1"
typescript: "npm:5.3.3" typescript: "npm:5.3.3"
use-context-selector: "npm:^2.0.0"
use-debounce: "npm:^10.0.0" use-debounce: "npm:^10.0.0"
uuid: "npm:^9.0.0" uuid: "npm:^9.0.0"
vite: "npm:^5.4.0" vite: "npm:^5.4.0"
@ -56763,16 +56762,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"use-context-selector@npm:^2.0.0":
version: 2.0.0
resolution: "use-context-selector@npm:2.0.0"
peerDependencies:
react: ">=18.0.0"
scheduler: ">=0.19.0"
checksum: 10c0/4eb6054ab8996ae8b3f87f9d102e576066e5a8b9db5db2c891128ae920bd64bcdcb4e93a13bc99658ef16280929a8331fc8ac45177f4acd716c425b1bc31135a
languageName: node
linkType: hard
"use-debounce@npm:^10.0.0": "use-debounce@npm:^10.0.0":
version: 10.0.2 version: 10.0.2
resolution: "use-debounce@npm:10.0.2" resolution: "use-debounce@npm:10.0.2"