From 25522752e4302338839a10f6da63f861779fc14a Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Fri, 20 Sep 2024 16:13:29 +0200 Subject: [PATCH] View module refactor with atomic recoil component instance states (#6810) This PR refactors the view module to implement utils that avoid having to create hooks to inject the scope id in the states, like `useViewStates`, each componentState will know its unique related InstanceContext (which holds the instanceId), and thus will be able to retrieve it itself. We keep the naming componentState as it reflects the fact that those states are tied to instances of a component (or its children). We introduce the instance word where it is needed, in place of scopeId for example, to precise the fact that we handle instances of component state, one for each instance of a component. For example, the currentViewId is a state that is tied to an instance of the ViewBar, but as we can switch between views, we want currentViewId to be a componentState tied to an instance of the ViewBar component. This PR also refactors view filter and sort states to fix this issue : https://github.com/twentyhq/twenty/issues/6837 and other problems involving resetting those states between page navigation. Fixes https://github.com/twentyhq/twenty/issues/6837 --------- Co-authored-by: Charles Bochet --- .../components/ObjectFilterDropdownButton.tsx | 14 +- .../ObjectFilterDropdownFilterSelect.tsx | 10 +- .../ObjectFilterDropdownRecordSelect.tsx | 9 +- ...SingleEntityObjectFilterDropdownButton.tsx | 10 +- .../MultipleFiltersDropdownButton.stories.tsx | 21 ++- .../__tests__/useFilterDropdown.test.tsx | 10 +- .../hooks/useFilterDropdown.ts | 6 - .../hooks/useFilterDropdownStates.ts | 7 - ...bjectFilterDropdownScopeInternalContext.ts | 4 +- .../hooks/__tests__/useSortDropdown.test.tsx | 10 +- .../hooks/useObjectSortDropdown.ts | 9 +- .../hooks/useSortDropdown.ts | 3 +- .../hooks/useSortDropdownStates.ts | 7 - .../ObjectSortDropdownScopeInternalContext.ts | 4 +- .../RecordBoardScopeInternalContext.ts | 4 +- .../record-field/hooks/useInitDraftValueV2.ts | 2 +- .../RecordFieldInputScopeInternalContext.ts | 4 +- .../components/RecordIndexContainer.tsx | 155 ++++++++-------- .../RecordIndexRemoveSortingModal.tsx | 6 +- .../RecordIndexTableContainerEffect.tsx | 10 +- .../hooks/useHandleIndexIdentifierClick.ts | 9 +- .../hooks/useHandleToggleColumnFilter.ts | 4 +- .../hooks/useHandleToggleColumnSort.ts | 4 +- .../hooks/useHandleToggleTrashColumnFilter.ts | 4 +- .../hooks/__tests__/useTableData.test.tsx | 38 ++-- .../hooks/useRecordIndexOptionsForBoard.ts | 6 +- .../components/RecordTableActionBar.tsx | 11 +- .../RecordTableEmptyStateSoftDelete.tsx | 7 +- .../useSelectedTableCellEditMode.test.tsx | 4 +- .../RecordTableScopeInternalContext.ts | 4 +- ...dTableFetchedAllRecordsComponentStateV2.ts | 4 +- ...isRecordTableScrolledLeftComponentState.ts | 4 +- .../isRecordTableScrolledTopComponentState.ts | 4 +- .../RelationPickerScopeInternalContext.ts | 4 +- .../SignInBackgroundMockContainer.tsx | 47 ++--- .../DialogManagerScopeInternalContext.ts | 4 +- .../SnackBarManagerScopeInternalContext.ts | 4 +- .../DropdownScopeInternalContext.ts | 4 +- .../SelectableListScopeInternalContext.ts | 4 +- .../modules/ui/layout/tab/hooks/useTabList.ts | 2 +- .../TabListScopeInternalContext.ts | 4 +- .../hooks/useRecoilScopedStateV2.ts | 4 +- .../utils/createScopeInternalContext.ts | 7 +- .../types/RecoilScopedSelector.ts | 4 +- .../recoil-scope/types/RecoilScopedState.ts | 4 +- .../utils/getScopeIdFromComponentId.ts | 2 +- .../useAvailableComponentInstanceIdOrThrow.ts | 24 +++ .../hooks/useComponentInstanceStateContext.ts | 12 ++ .../hooks/useRecoilCallbackState.ts | 27 +++ .../useRecoilComponentCallbackStateV2.ts | 128 ++++++++++++++ .../hooks/useRecoilComponentFamilyValueV2.ts | 52 ++++++ .../hooks/useRecoilComponentState.ts | 28 +++ .../hooks/useRecoilComponentStateV2.ts | 26 +++ .../hooks/useRecoilComponentValue.ts | 4 +- .../hooks/useRecoilComponentValueV2.ts | 26 +++ .../hooks/useScopeIdFromStateContext.ts | 4 +- .../useSetRecoilComponentFamilyStateV2.ts | 52 ++++++ .../hooks/useSetRecoilComponentState.ts | 4 +- .../hooks/useSetRecoilComponentStateV2.ts | 26 +++ .../ComponentFamilyReadOnlySelectorV2.ts | 14 ++ .../types/ComponentFamilySelectorV2.ts | 14 ++ .../types/ComponentFamilyStateKeyV2.ts | 7 + .../types/ComponentFamilyStateV2.ts | 14 ++ .../types/ComponentInstanceStateContext.ts | 4 + .../types/ComponentReadOnlySelectorV2.ts | 11 ++ .../types/ComponentSelectorV2.ts | 11 ++ .../component-state/types/ComponentState.ts | 8 - .../types/ComponentStateKey.ts | 3 - .../types/ComponentStateKeyV2.ts | 3 + .../types/ComponentStateTypeV2.ts | 7 + .../component-state/types/ComponentStateV2.ts | 11 ++ .../types/RecoilComponentState.ts | 9 + .../types/RecoilComponentStateKey.ts | 3 + .../utils/createComponentFamilySelectorV2.ts | 58 ++++++ .../utils/createComponentFamilyStateV2.ts | 30 ++-- .../utils/createComponentInstanceContext.ts | 13 ++ .../utils/createComponentReadOnlySelector.ts | 6 +- .../utils/createComponentSelector.ts | 8 +- .../utils/createComponentSelectorV2.ts | 47 +++++ .../utils/createComponentState.ts | 4 +- .../utils/createComponentStateV2.ts | 28 ++- .../utils/createComponentStateV2_alpha.ts | 38 ++++ .../utils/createEventContext.ts | 9 + .../utils/extractComponentReadOnlySelector.ts | 4 +- .../utils/extractComponentSelector.ts | 4 +- .../utils/extractComponentState.ts | 4 +- .../globalComponentInstanceContextMap.ts | 21 +++ .../EditableFilterDropdownButton.tsx | 21 ++- .../views/components/EditableSortChip.tsx | 10 +- .../components/QueryParamsFiltersEffect.tsx | 28 +-- .../components/QueryParamsViewIdEffect.tsx | 27 +-- .../components/UpdateViewButtonGroup.tsx | 55 +++--- .../views/components/VariantFilterChip.tsx | 6 +- .../src/modules/views/components/ViewBar.tsx | 10 +- .../views/components/ViewBarDetails.tsx | 51 +++--- .../views/components/ViewBarEffect.tsx | 27 +-- .../views/components/ViewBarFilterEffect.tsx | 25 ++- .../views/components/ViewBarSortEffect.tsx | 30 ++-- .../views/events/contexts/ViewEventContext.ts | 8 + .../views/hooks/internal/useViewStates.ts | 91 ---------- .../src/modules/views/hooks/useChangeView.ts | 24 +++ .../views/hooks/useCombinedViewFilters.ts | 162 ----------------- .../views/hooks/useCombinedViewSorts.ts | 159 ----------------- .../hooks/useCreateViewFiltersAndSorts.ts | 34 ++++ .../hooks/useCreateViewFromCurrentView.ts | 122 +++++++++++++ .../hooks/useDeleteCombinedViewFilters.ts | 101 +++++++++++ .../views/hooks/useDeleteCombinedViewSorts.ts | 100 +++++++++++ .../src/modules/views/hooks/useDeleteView.ts | 18 ++ .../views/hooks/useGetCombinedViewFilters.ts | 67 +++++++ .../views/hooks/useGetCombinedViewSorts.ts | 68 +++++++ .../modules/views/hooks/useGetCurrentView.ts | 91 ++++++---- .../src/modules/views/hooks/useHandleViews.ts | 165 ----------------- .../src/modules/views/hooks/useInitViewBar.ts | 43 ++--- .../views/hooks/useResetCurrentView.ts | 32 ---- .../views/hooks/useResetUnsavedViewStates.ts | 52 ++++++ .../views/hooks/useSaveCurrentViewFields.ts | 25 ++- .../useSaveCurrentViewFiltersAndSorts.ts | 83 ++++++--- .../hooks/useSetRecordCountInCurrentView.ts | 12 +- .../views/hooks/useUpdateCurrentView.ts | 40 +++++ .../src/modules/views/hooks/useUpdateView.ts | 27 +++ .../hooks/useUpsertCombinedViewFilters.ts | 131 ++++++++++++++ .../views/hooks/useUpsertCombinedViewSorts.ts | 123 +++++++++++++ .../src/modules/views/scopes/ViewScope.tsx | 32 ---- .../init-effect/ViewScopeInitEffect.tsx | 24 --- .../ViewScopeInternalContext.ts | 7 - ...availableFieldDefinitionsComponentState.ts | 6 +- ...vailableFilterDefinitionsComponentState.ts | 6 +- .../availableSortDefinitionsComponentState.ts | 6 +- .../contexts/ViewComponentInstanceContext.ts | 3 + .../states/currentViewIdComponentState.ts | 6 +- .../entityCountInCurrentViewComponentState.ts | 6 +- .../isCurrentViewIndexComponentState.ts | 6 +- .../isPersistingViewFieldsComponentState.ts | 6 +- .../states/isViewBarExpandedComponentState.ts | 6 +- .../onCurrentViewChangeComponentState.ts | 9 - .../canPersistViewComponentFamilySelector.ts | 42 +++++ .../canPersistViewComponentSelector.ts | 21 --- .../canResetViewComponentSelector.ts | 22 --- ...DeleteViewFilterIdsComponentFamilyState.ts | 9 + ...avedToDeleteViewFilterIdsComponentState.ts | 8 - ...ToDeleteViewSortIdsComponentFamilyState.ts | 9 + ...nsavedToDeleteViewSortIdsComponentState.ts | 8 - ...ToUpsertViewFiltersComponentFamilyState.ts | 10 ++ ...nsavedToUpsertViewFiltersComponentState.ts | 10 -- ...edToUpsertViewSortsComponentFamilyState.ts | 10 ++ .../unsavedToUpsertViewSortsComponentState.ts | 10 -- .../viewObjectMetadataIdComponentState.ts | 6 +- ...ewFilters.ts => getCombinedViewFilters.ts} | 2 +- ...edViewSorts.ts => getCombinedViewSorts.ts} | 2 +- ...nt.tsx => ViewPickerContentCreateMode.tsx} | 166 ++++++++---------- .../components/ViewPickerContentEditMode.tsx | 100 +++++++++++ .../components/ViewPickerContentEffect.tsx | 90 ++++++++++ .../components/ViewPickerCreateButton.tsx | 86 +++++++++ .../ViewPickerCreateOrEditContentEffect.tsx | 83 --------- .../components/ViewPickerDropdown.tsx | 31 ++-- ...ditButton.tsx => ViewPickerEditButton.tsx} | 37 ++-- .../ViewPickerIconAndNameContainer.tsx | 10 ++ .../components/ViewPickerListContent.tsx | 21 ++- .../ViewPickerSaveButtonContainer.tsx | 9 + .../components/ViewPickerSelectContainer.tsx | 11 ++ .../hooks/useCloseAndResetViewPicker.ts | 9 +- .../hooks/useCreateViewFromCurrentState.ts | 118 +++++++++++++ .../hooks/useDeleteViewFromCurrentState.ts | 78 ++++++++ .../hooks/useGetAvailableFieldsForKanban.ts | 9 +- .../hooks/useUpdateViewFromCurrentState.ts | 80 +++++++++ .../view-picker/hooks/useViewPickerMode.ts | 13 +- .../hooks/useViewPickerPersistView.ts | 132 -------------- .../view-picker/hooks/useViewPickerStates.ts | 55 ------ .../viewPickerInputNameComponentState.ts | 14 +- .../states/viewPickerIsDirtyComponentState.ts | 6 +- .../viewPickerIsPersistingComponentState.ts | 6 +- ...ckerKanbanFieldMetadataIdComponentState.ts | 6 +- .../states/viewPickerModeComponentState.ts | 16 +- ...viewPickerReferenceViewIdComponentState.ts | 6 +- .../viewPickerSelectedIconComponentState.ts | 6 +- .../states/viewPickerTypeComponentState.ts | 6 +- .../views/view-picker/types/ViewPickerMode.ts | 5 + 177 files changed, 3132 insertions(+), 1745 deletions(-) create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilCallbackState.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentState.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilySelectorV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentInstanceStateContext.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentSelectorV2.ts delete mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentState.ts delete mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKey.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKeyV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateTypeV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentState.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentStateKey.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentInstanceContext.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2_alpha.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createEventContext.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap.ts create mode 100644 packages/twenty-front/src/modules/views/events/contexts/ViewEventContext.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/internal/useViewStates.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useChangeView.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/useCombinedViewFilters.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/useCombinedViewSorts.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useCreateViewFiltersAndSorts.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useDeleteCombinedViewFilters.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useDeleteCombinedViewSorts.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useDeleteView.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useGetCombinedViewFilters.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useGetCombinedViewSorts.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/useHandleViews.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/useResetCurrentView.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useResetUnsavedViewStates.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useUpdateCurrentView.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useUpdateView.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useUpsertCombinedViewFilters.ts create mode 100644 packages/twenty-front/src/modules/views/hooks/useUpsertCombinedViewSorts.ts delete mode 100644 packages/twenty-front/src/modules/views/scopes/ViewScope.tsx delete mode 100644 packages/twenty-front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx delete mode 100644 packages/twenty-front/src/modules/views/scopes/scope-internal-context/ViewScopeInternalContext.ts create mode 100644 packages/twenty-front/src/modules/views/states/contexts/ViewComponentInstanceContext.ts delete mode 100644 packages/twenty-front/src/modules/views/states/onCurrentViewChangeComponentState.ts create mode 100644 packages/twenty-front/src/modules/views/states/selectors/canPersistViewComponentFamilySelector.ts delete mode 100644 packages/twenty-front/src/modules/views/states/selectors/canPersistViewComponentSelector.ts delete mode 100644 packages/twenty-front/src/modules/views/states/selectors/canResetViewComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/views/states/unsavedToDeleteViewFilterIdsComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/views/states/unsavedToDeleteViewFilterIdsComponentState.ts create mode 100644 packages/twenty-front/src/modules/views/states/unsavedToDeleteViewSortIdsComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/views/states/unsavedToDeleteViewSortIdsComponentState.ts create mode 100644 packages/twenty-front/src/modules/views/states/unsavedToUpsertViewFiltersComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/views/states/unsavedToUpsertViewFiltersComponentState.ts create mode 100644 packages/twenty-front/src/modules/views/states/unsavedToUpsertViewSortsComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/views/states/unsavedToUpsertViewSortsComponentState.ts rename packages/twenty-front/src/modules/views/utils/{combinedViewFilters.ts => getCombinedViewFilters.ts} (96%) rename packages/twenty-front/src/modules/views/utils/{combinedViewSorts.ts => getCombinedViewSorts.ts} (96%) rename packages/twenty-front/src/modules/views/view-picker/components/{ViewPickerCreateOrEditContent.tsx => ViewPickerContentCreateMode.tsx} (51%) create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEditMode.tsx create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEffect.tsx create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateButton.tsx delete mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContentEffect.tsx rename packages/twenty-front/src/modules/views/view-picker/components/{ViewPickerCreateOrEditButton.tsx => ViewPickerEditButton.tsx} (53%) create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerIconAndNameContainer.tsx create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerSaveButtonContainer.tsx create mode 100644 packages/twenty-front/src/modules/views/view-picker/components/ViewPickerSelectContainer.tsx create mode 100644 packages/twenty-front/src/modules/views/view-picker/hooks/useCreateViewFromCurrentState.ts create mode 100644 packages/twenty-front/src/modules/views/view-picker/hooks/useDeleteViewFromCurrentState.ts create mode 100644 packages/twenty-front/src/modules/views/view-picker/hooks/useUpdateViewFromCurrentState.ts delete mode 100644 packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerPersistView.ts delete mode 100644 packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerStates.ts create mode 100644 packages/twenty-front/src/modules/views/view-picker/types/ViewPickerMode.ts diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx index 3b56727837..9abc6b7842 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx @@ -1,9 +1,8 @@ -import { useRecoilValue } from 'recoil'; - -import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton'; import { SingleEntityObjectFilterDropdownButton } from './SingleEntityObjectFilterDropdownButton'; @@ -16,12 +15,9 @@ export const ObjectFilterDropdownButton = ({ filterDropdownId, hotkeyScope, }: ObjectFilterDropdownButtonProps) => { - const { availableFilterDefinitionsState } = useFilterDropdown({ - filterDropdownId: filterDropdownId, - }); - - const availableFilterDefinitions = useRecoilValue( - availableFilterDefinitionsState, + const availableFilterDefinitions = useRecoilComponentValueV2( + availableFilterDefinitionsComponentState, + filterDropdownId, ); const hasOnlyOneEntityFilter = diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index caf6db6212..59ee04d925 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -1,8 +1,6 @@ import styled from '@emotion/styled'; import { useState } from 'react'; -import { useRecoilValue } from 'recoil'; -import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem'; @@ -12,6 +10,8 @@ import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { isDefined } from 'twenty-ui'; export const StyledInput = styled.input` @@ -43,10 +43,8 @@ export const StyledInput = styled.input` export const ObjectFilterDropdownFilterSelect = () => { const [searchText, setSearchText] = useState(''); - const { availableFilterDefinitionsState } = useFilterDropdown(); - - const availableFilterDefinitions = useRecoilValue( - availableFilterDefinitionsState, + const availableFilterDefinitions = useRecoilComponentValueV2( + availableFilterDefinitionsComponentState, ); const sortedAvailableFilterDefinitions = [...availableFilterDefinitions] diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx index 7546dc51c2..ddaaf2e6ad 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx @@ -7,7 +7,7 @@ import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types import { MultipleRecordSelectDropdown } from '@/object-record/select/components/MultipleRecordSelectDropdown'; import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect'; import { SelectableRecord } from '@/object-record/select/types/SelectableRecord'; -import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters'; +import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { isDefined } from '~/utils/isDefined'; @@ -17,6 +17,7 @@ export const MAX_RECORDS_TO_DISPLAY = 3; type ObjectFilterDropdownRecordSelectProps = { viewComponentId?: string; }; + export const ObjectFilterDropdownRecordSelect = ({ viewComponentId, }: ObjectFilterDropdownRecordSelectProps) => { @@ -31,7 +32,9 @@ export const ObjectFilterDropdownRecordSelect = ({ emptyFilterButKeepDefinition, } = useFilterDropdown(); - const { removeCombinedViewFilter } = useCombinedViewFilters(viewComponentId); + const { deleteCombinedViewFilter } = + useDeleteCombinedViewFilters(viewComponentId); + const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(viewComponentId); @@ -78,7 +81,7 @@ export const ObjectFilterDropdownRecordSelect = ({ if (newSelectedRecordIds.length === 0) { emptyFilterButKeepDefinition(); - removeCombinedViewFilter(fieldId); + deleteCombinedViewFilter(fieldId); return; } diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx index 5f57a990d3..ddfb5125b9 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { useTheme } from '@emotion/react'; +import React from 'react'; import { useRecoilValue } from 'recoil'; import { IconChevronDown } from 'twenty-ui'; @@ -11,8 +11,9 @@ import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/Styl import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { getOperandsForFilterType } from '../utils/getOperandsForFilterType'; - import { GenericEntityFilterChip } from './GenericEntityFilterChip'; import { ObjectFilterDropdownRecordSelect } from './ObjectFilterDropdownRecordSelect'; import { ObjectFilterDropdownSearchInput } from './ObjectFilterDropdownSearchInput'; @@ -25,14 +26,13 @@ export const SingleEntityObjectFilterDropdownButton = ({ hotkeyScope: HotkeyScope; }) => { const { - availableFilterDefinitionsState, selectedFilterState, setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, } = useFilterDropdown(); - const availableFilterDefinitions = useRecoilValue( - availableFilterDefinitionsState, + const availableFilterDefinitions = useRecoilComponentValueV2( + availableFilterDefinitionsComponentState, ); const selectedFilter = useRecoilValue(selectedFilterState); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx index c099814932..b97deda7b5 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx @@ -2,8 +2,10 @@ import { Meta, StoryObj } from '@storybook/react'; import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton'; -import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { within } from '@storybook/test'; import { ComponentDecorator } from 'twenty-ui'; import { FieldMetadataType } from '~/generated/graphql'; @@ -17,9 +19,12 @@ const meta: Meta = { component: MultipleFiltersDropdownButton, decorators: [ (Story) => { - const { setAvailableFilterDefinitions } = useFilterDropdown({ - filterDropdownId: 'entity-tasks-filter-scope', - }); + const instanceId = 'entity-tasks-filter-scope'; + const setAvailableFilterDefinitions = useSetRecoilComponentStateV2( + availableFilterDefinitionsComponentState, + instanceId, + ); + setAvailableFilterDefinitions([ { fieldMetadataId: '1', @@ -47,9 +52,11 @@ const meta: Meta = { }, ]); return ( - - - + + + + + ); }, ObjectMetadataItemsDecorator, diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/__tests__/useFilterDropdown.test.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/__tests__/useFilterDropdown.test.tsx index 91aed0b40a..b69584cb39 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/__tests__/useFilterDropdown.test.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/__tests__/useFilterDropdown.test.tsx @@ -6,6 +6,8 @@ import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/ import { useFilterDropdownStates } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdownStates'; import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; const filterDropdownId = 'filterDropdownId'; @@ -35,11 +37,13 @@ describe('useFilterDropdown', () => { it('should set availableFilterDefinitions', async () => { const { result } = renderHook(() => { useFilterDropdown({ filterDropdownId }); - const { availableFilterDefinitionsState } = - useFilterDropdownStates(filterDropdownId); const [availableFilterDefinitions, setAvailableFilterDefinitions] = - useRecoilState(availableFilterDefinitionsState); + useRecoilComponentStateV2( + availableFilterDefinitionsComponentState, + filterDropdownId, + ); + return { availableFilterDefinitions, setAvailableFilterDefinitions }; }, renderHookConfig); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts index 8325af5e80..b09d33a383 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdown.ts @@ -18,7 +18,6 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { ); const { - availableFilterDefinitionsState, filterDefinitionUsedInDropdownState, objectFilterDropdownSearchInputState, objectFilterDropdownSelectedRecordIdsState, @@ -73,9 +72,6 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { ], ); - const setAvailableFilterDefinitions = useSetRecoilState( - availableFilterDefinitionsState, - ); const setSelectedFilter = useSetRecoilState(selectedFilterState); const setSelectedOperandInDropdown = useSetRecoilState( selectedOperandInDropdownState, @@ -106,7 +102,6 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { resetFilter, setSelectedFilter, setSelectedOperandInDropdown, - setAvailableFilterDefinitions, setFilterDefinitionUsedInDropdown, setObjectFilterDropdownSearchInput, // setObjectFilterDropdownSelectedEntityId, @@ -116,7 +111,6 @@ export const useFilterDropdown = (props?: UseFilterDropdownProps) => { setIsObjectFilterDropdownUnfolded, setOnFilterSelect, emptyFilterButKeepDefinition, - availableFilterDefinitionsState, filterDefinitionUsedInDropdownState, objectFilterDropdownSearchInputState, // objectFilterDropdownSelectedEntityIdState, diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdownStates.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdownStates.ts index 82888840c7..8d9e5f1d08 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdownStates.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useFilterDropdownStates.ts @@ -8,14 +8,8 @@ import { onFilterSelectComponentState } from '@/object-record/object-filter-drop import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; -import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; export const useFilterDropdownStates = (scopeId: string) => { - const availableFilterDefinitionsState = extractComponentState( - availableFilterDefinitionsComponentState, - scopeId, - ); - const filterDefinitionUsedInDropdownState = extractComponentState( filterDefinitionUsedInDropdownComponentState, scopeId, @@ -63,7 +57,6 @@ export const useFilterDropdownStates = (scopeId: string) => { ); return { - availableFilterDefinitionsState, filterDefinitionUsedInDropdownState, objectFilterDropdownSearchInputState, objectFilterDropdownSelectedRecordIdsState, diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext.ts index 1c29844bd5..be81417c1c 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/scopes/scope-internal-context/ObjectFilterDropdownScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type ObjectFilterDropdownScopeInternalContextProps = ComponentStateKey; +type ObjectFilterDropdownScopeInternalContextProps = RecoilComponentStateKey; export const ObjectFilterDropdownScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/__tests__/useSortDropdown.test.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/__tests__/useSortDropdown.test.tsx index 6bf6ddf0d5..64de66d895 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/__tests__/useSortDropdown.test.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/__tests__/useSortDropdown.test.tsx @@ -6,6 +6,8 @@ import { useSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useS import { useSortDropdownStates } from '@/object-record/object-sort-dropdown/hooks/useSortDropdownStates'; import { Sort } from '@/object-record/object-sort-dropdown/types/Sort'; import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( {children} @@ -24,11 +26,13 @@ describe('useSortDropdown', () => { it('should set availableSortDefinitions', async () => { const { result } = renderHook(() => { useSortDropdown({ sortDropdownId }); - const { availableSortDefinitionsState } = - useSortDropdownStates(sortDropdownId); + // TODO: verify this instance id works const [availableSortDefinitions, setAvailableSortDefinitions] = - useRecoilState(availableSortDefinitionsState); + useRecoilComponentStateV2( + availableSortDefinitionsComponentState, + sortDropdownId, + ); return { availableSortDefinitions, diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useObjectSortDropdown.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useObjectSortDropdown.ts index 4a797a259a..2cbe44c58a 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useObjectSortDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useObjectSortDropdown.ts @@ -7,6 +7,8 @@ import selectedSortDirectionState from '@/object-record/object-sort-dropdown/sta import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState'; import { OBJECT_SORT_DROPDOWN_ID, VIEW_SORT_DROPDOWN_ID, @@ -41,7 +43,6 @@ export const useObjectSortDropdown = () => { }; const { - availableSortDefinitionsState, onSortSelectState, isSortSelectedState, objectSortDropdownSearchInputState, @@ -52,8 +53,10 @@ export const useObjectSortDropdown = () => { }); const isSortSelected = useRecoilValue(isSortSelectedState); - const availableSortDefinitions = useRecoilValue( - availableSortDefinitionsState, + + const availableSortDefinitions = useRecoilComponentValueV2( + availableSortDefinitionsComponentState, + VIEW_SORT_DROPDOWN_ID, ); const onSortSelect = useRecoilValue(onSortSelectState); diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdown.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdown.ts index 44373f1fa4..3a857e28ca 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdown.ts @@ -14,8 +14,8 @@ export const useSortDropdown = (props?: UseSortProps) => { ObjectSortDropdownScopeInternalContext, props?.sortDropdownId, ); + const { - availableSortDefinitionsState, isSortSelectedState, onSortSelectState, objectSortDropdownSearchInputState, @@ -35,7 +35,6 @@ export const useSortDropdown = (props?: UseSortProps) => { return { scopeId, - availableSortDefinitionsState, isSortSelectedState, onSortSelectState, objectSortDropdownSearchInputState, diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdownStates.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdownStates.ts index b36788076b..40eb5623e6 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdownStates.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useSortDropdownStates.ts @@ -2,14 +2,8 @@ import { isSortSelectedComponentState } from '@/object-record/object-sort-dropdo import { objectSortDropdownSearchInputComponentState } from '@/object-record/object-sort-dropdown/states/objectSortDropdownSearchInputComponentState'; import { onSortSelectComponentState } from '@/object-record/object-sort-dropdown/states/onSortSelectScopedState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; -import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState'; export const useSortDropdownStates = (scopeId: string) => { - const availableSortDefinitionsState = extractComponentState( - availableSortDefinitionsComponentState, - scopeId, - ); - const isSortSelectedState = extractComponentState( isSortSelectedComponentState, scopeId, @@ -26,7 +20,6 @@ export const useSortDropdownStates = (scopeId: string) => { ); return { - availableSortDefinitionsState, isSortSelectedState, onSortSelectState, objectSortDropdownSearchInputState, diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/scopes/scope-internal-context/ObjectSortDropdownScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/scopes/scope-internal-context/ObjectSortDropdownScopeInternalContext.ts index 8454813f3d..35a7798bc5 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/scopes/scope-internal-context/ObjectSortDropdownScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/scopes/scope-internal-context/ObjectSortDropdownScopeInternalContext.ts @@ -1,9 +1,9 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; import { Sort } from '../../types/Sort'; -type ObjectSortDropdownScopeInternalContextProps = ComponentStateKey & { +type ObjectSortDropdownScopeInternalContextProps = RecoilComponentStateKey & { onSortSelect?: (sort: Sort) => void; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext.ts index 22aeae0edc..44ac1e08e1 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext.ts @@ -2,9 +2,9 @@ import { RecordBoardColumnDefinition } from '@/object-record/record-board/types/ import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type RecordBoardScopeInternalContextProps = ComponentStateKey & { +type RecordBoardScopeInternalContextProps = RecoilComponentStateKey & { onFieldsChange: (fields: FieldDefinition[]) => void; onColumnsChange: (column: RecordBoardColumnDefinition[]) => void; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useInitDraftValueV2.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useInitDraftValueV2.ts index 5c7f8fb2da..f3847113fb 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/useInitDraftValueV2.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/useInitDraftValueV2.ts @@ -27,7 +27,7 @@ export const useInitDraftValueV2 = () => { const recordFieldInputScopeId = `${getRecordFieldInputId( recordId, fieldDefinition?.metadata?.fieldName, - )}-scope`; + )}`; const getDraftValueSelector = extractComponentSelector< FieldInputDraftValue | undefined diff --git a/packages/twenty-front/src/modules/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext.ts index eebf6c22d5..94769746b2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type RecordFieldInputScopeInternalContextProps = ComponentStateKey; +type RecordFieldInputScopeInternalContextProps = RecoilComponentStateKey; export const RecordFieldInputScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx index e8cdaa20f5..3af3237929 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx @@ -24,6 +24,7 @@ import { RecordFieldValueSelectorContextProvider } from '@/object-record/record- import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; import { ViewBar } from '@/views/components/ViewBar'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewField } from '@/views/types/ViewField'; import { ViewType } from '@/views/types/ViewType'; import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions'; @@ -107,81 +108,91 @@ export const RecordIndexContainer = () => { return ( - - - - - } - onCurrentViewChange={(view) => { - if (!view) { - return; + + + + + } + onCurrentViewChange={(view) => { + if (!view) { + return; + } - onViewFieldsChange(view.viewFields); - setTableFilters( - mapViewFiltersToFilters(view.viewFilters, filterDefinitions), - ); - setRecordIndexFilters( - mapViewFiltersToFilters(view.viewFilters, filterDefinitions), - ); - setTableSorts( - mapViewSortsToSorts(view.viewSorts, sortDefinitions), - ); - setRecordIndexSorts( - mapViewSortsToSorts(view.viewSorts, sortDefinitions), - ); - setRecordIndexViewType(view.type); - setRecordIndexViewKanbanFieldMetadataIdState( - view.kanbanFieldMetadataId, - ); - setRecordIndexIsCompactModeActive(view.isCompact); - }} - /> - - - + onViewFieldsChange(view.viewFields); + setTableFilters( + mapViewFiltersToFilters( + view.viewFilters, + filterDefinitions, + ), + ); + setRecordIndexFilters( + mapViewFiltersToFilters( + view.viewFilters, + filterDefinitions, + ), + ); + setTableSorts( + mapViewSortsToSorts(view.viewSorts, sortDefinitions), + ); + setRecordIndexSorts( + mapViewSortsToSorts(view.viewSorts, sortDefinitions), + ); + setRecordIndexViewType(view.type); + setRecordIndexViewKanbanFieldMetadataIdState( + view.kanbanFieldMetadataId, + ); + setRecordIndexIsCompactModeActive(view.isCompact); + }} + /> + + + - {recordIndexViewType === ViewType.Table && ( - <> - - - - )} - {recordIndexViewType === ViewType.Kanban && ( - - - - - - )} - + {recordIndexViewType === ViewType.Table && ( + <> + + + + )} + {recordIndexViewType === ViewType.Kanban && ( + + + + + + )} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx index efe7e4cb92..1a16e7feee 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx @@ -2,7 +2,7 @@ import { useRecoilState } from 'recoil'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; -import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts'; +import { useDeleteCombinedViewSorts } from '@/views/hooks/useDeleteCombinedViewSorts'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; export const RecordIndexRemoveSortingModal = ({ @@ -21,11 +21,11 @@ export const RecordIndexRemoveSortingModal = ({ isRemoveSortingModalOpenState, ); - const { removeCombinedViewSort } = useCombinedViewSorts(recordTableId); + const { deleteCombinedViewSort } = useDeleteCombinedViewSorts(recordTableId); const handleRemoveClick = () => { fieldMetadataIds.forEach((id) => { - removeCombinedViewSort(id); + deleteCombinedViewSort(id); }); }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainerEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainerEffect.tsx index 32ae2ffc50..428537f694 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainerEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainerEffect.tsx @@ -8,8 +8,9 @@ import { useHandleToggleColumnFilter } from '@/object-record/record-index/hooks/ import { useHandleToggleColumnSort } from '@/object-record/record-index/hooks/useHandleToggleColumnSort'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; -import { useViewStates } from '@/views/hooks/internal/useViewStates'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; +import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState'; type RecordIndexTableContainerEffectProps = { objectNameSingular: string; @@ -50,9 +51,10 @@ export const RecordIndexTableContainerEffect = ({ const { tableRowIdsState, hasUserSelectedAllRowsState } = useRecordTableStates(recordTableId); - const { entityCountInCurrentViewState } = useViewStates(recordTableId); - const entityCountInCurrentView = useRecoilValue( - entityCountInCurrentViewState, + // TODO: verify this instance id works + const entityCountInCurrentView = useRecoilComponentValueV2( + entityCountInCurrentViewComponentState, + recordTableId, ); const hasUserSelectedAllRows = useRecoilValue(hasUserSelectedAllRowsState); const tableRowIds = useRecoilValue(tableRowIdsState); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts index a72fda64b9..2ea5bcdc41 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts @@ -1,8 +1,8 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { useNavigate } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; export const useHandleIndexIdentifierClick = ({ objectMetadataItem, @@ -13,10 +13,9 @@ export const useHandleIndexIdentifierClick = ({ }) => { const navigate = useNavigate(); - const currentViewId = useRecoilValue( - currentViewIdComponentState({ - scopeId: recordIndexId, - }), + const currentViewId = useRecoilComponentValueV2( + currentViewIdComponentState, + recordIndexId, ); const handleIndexIdentifierClick = (recordId: string) => { diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts index 18997fbadb..41d4fc49d9 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts @@ -7,7 +7,7 @@ import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldM import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; import { getOperandsForFilterType } from '@/object-record/object-filter-dropdown/utils/getOperandsForFilterType'; import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; -import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters'; +import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { isDefined } from '~/utils/isDefined'; type UseHandleToggleColumnFilterProps = { @@ -26,7 +26,7 @@ export const useHandleToggleColumnFilter = ({ const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); - const { upsertCombinedViewFilter } = useCombinedViewFilters(viewBarId); + const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters(viewBarId); const { openDropdown } = useDropdownV2(); const handleToggleColumnFilter = useCallback( diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts index a5253a49e8..b285fe3450 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { Sort } from '@/object-record/object-sort-dropdown/types/Sort'; -import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts'; +import { useUpsertCombinedViewSorts } from '@/views/hooks/useUpsertCombinedViewSorts'; import { isDefined } from '~/utils/isDefined'; type UseHandleToggleColumnSortProps = { @@ -22,7 +22,7 @@ export const useHandleToggleColumnSort = ({ const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); - const { upsertCombinedViewSort } = useCombinedViewSorts(viewBarId); + const { upsertCombinedViewSort } = useUpsertCombinedViewSorts(viewBarId); const handleToggleColumnSort = useCallback( (fieldMetadataId: string) => { diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts index bbe862c0a6..31dac4ae70 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleTrashColumnFilter.ts @@ -6,7 +6,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; -import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters'; +import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useRecoilCallback } from 'recoil'; import { isDefined } from '~/utils/isDefined'; @@ -27,7 +27,7 @@ export const useHandleToggleTrashColumnFilter = ({ const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); - const { upsertCombinedViewFilter } = useCombinedViewFilters(viewBarId); + const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters(viewBarId); const { isSoftDeleteActiveState } = useRecordTableStates(viewBarId); const handleToggleTrashColumnFilter = useCallback(() => { diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useTableData.test.tsx b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useTableData.test.tsx index 3d0ce75a0b..bac46e6523 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useTableData.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useTableData.test.tsx @@ -1,10 +1,10 @@ -import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; import { act, renderHook, waitFor } from '@testing-library/react'; import { percentage, sleep, useTableData } from '../useTableData'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState'; import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard'; +import { SnackBarManagerScopeInternalContext } from '@/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { ViewType } from '@/views/types/ViewType'; import { MockedProvider, MockedResponse } from '@apollo/client/testing'; @@ -148,15 +148,19 @@ const mocks: MockedResponse[] = [ ]; const Wrapper = ({ children }: { children: ReactNode }) => ( - - - + + + {children} - - - + + + ); const graphqlEmptyResponse = [ @@ -174,15 +178,19 @@ const graphqlEmptyResponse = [ ]; const WrapperWithEmptyResponse = ({ children }: { children: ReactNode }) => ( - - - + + + {children} - - - + + + ); describe('useTableData', () => { @@ -191,13 +199,13 @@ describe('useTableData', () => { describe('data fetching', () => { it('should handle no records', async () => { const callback = jest.fn(); + const { result } = renderHook( () => useTableData({ recordIndexId, objectNameSingular, callback, - delayMs: 0, viewType: ViewType.Kanban, }), @@ -209,7 +217,7 @@ describe('useTableData', () => { }); await waitFor(() => { - expect(callback).toHaveBeenCalledWith([], []); + expect(callback).not.toHaveBeenCalled(); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts index 9e1c2b28b6..c3d5e87d97 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts @@ -1,5 +1,5 @@ -import { useCallback, useMemo } from 'react'; import { OnDragEndResponder } from '@hello-pangea/dnd'; +import { useCallback, useMemo } from 'react'; import { useRecoilState } from 'recoil'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; @@ -8,8 +8,8 @@ import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoar import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; -import { useHandleViews } from '@/views/hooks/useHandleViews'; import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields'; +import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { GraphQLView } from '@/views/types/GraphQLView'; import { mapBoardFieldDefinitionsToViewFields } from '@/views/utils/mapBoardFieldDefinitionsToViewFields'; import { mapArrayToObject } from '~/utils/array/mapArrayToObject'; @@ -31,7 +31,7 @@ export const useRecordIndexOptionsForBoard = ({ useRecoilState(recordIndexFieldDefinitionsState); const { saveViewFields } = useSaveCurrentViewFields(viewBarId); - const { updateCurrentView } = useHandleViews(viewBarId); + const { updateCurrentView } = useUpdateCurrentView(viewBarId); const { isCompactModeActiveState } = useRecordBoard(recordBoardId); const [isCompactModeActive, setIsCompactModeActive] = useRecoilState( diff --git a/packages/twenty-front/src/modules/object-record/record-table/action-bar/components/RecordTableActionBar.tsx b/packages/twenty-front/src/modules/object-record/record-table/action-bar/components/RecordTableActionBar.tsx index f41025398c..0b2c810bc1 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/action-bar/components/RecordTableActionBar.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/action-bar/components/RecordTableActionBar.tsx @@ -2,7 +2,8 @@ import { useRecoilValue } from 'recoil'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; import { ActionBar } from '@/ui/navigation/action-bar/components/ActionBar'; -import { useViewStates } from '@/views/hooks/internal/useViewStates'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState'; export const RecordTableActionBar = ({ recordTableId, @@ -15,10 +16,12 @@ export const RecordTableActionBar = ({ hasUserSelectedAllRowsState, } = useRecordTableStates(recordTableId); - const { entityCountInCurrentViewState } = useViewStates(recordTableId); - const entityCountInCurrentView = useRecoilValue( - entityCountInCurrentViewState, + // TODO: verify this instance id works + const entityCountInCurrentView = useRecoilComponentValueV2( + entityCountInCurrentViewComponentState, + recordTableId, ); + const hasUserSelectedAllRows = useRecoilValue(hasUserSelectedAllRowsState); const tableRowIds = useRecoilValue(tableRowIdsState); const selectedRowIds = useRecoilValue(selectedRowIdsSelector()); diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx index 6954b6044f..71eb045abd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx @@ -5,7 +5,7 @@ import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/h import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; -import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters'; +import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters'; import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; @@ -13,7 +13,8 @@ export const RecordTableEmptyStateSoftDelete = () => { const { objectMetadataItem, objectNameSingular, recordTableId } = useContext(RecordTableContext); - const { removeCombinedViewFilter } = useCombinedViewFilters(recordTableId); + const { deleteCombinedViewFilter } = + useDeleteCombinedViewFilters(recordTableId); const { tableFiltersState } = useRecordTableStates(recordTableId); const tableFilters = useRecoilValue(tableFiltersState); @@ -24,7 +25,7 @@ export const RecordTableEmptyStateSoftDelete = () => { }); const handleButtonClick = async () => { - removeCombinedViewFilter( + deleteCombinedViewFilter( tableFilters.find( (filter) => filter.definition.label === 'Deleted at' && diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx index 1e7ec2088d..23b7e3b00d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useSelectedTableCellEditMode.test.tsx @@ -48,14 +48,14 @@ describe('useSelectedTableCellEditMode', () => { expect(mockCallbackInterface.set).toHaveBeenCalledWith( { - key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":0,"row":0},"scopeId":"yourScopeId-scope"}', + key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":0,"row":0},"scopeId":"yourScopeId"}', }, false, ); expect(mockCallbackInterface.set).toHaveBeenCalledWith( { - key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":5,"row":1},"scopeId":"yourScopeId-scope"}', + key: 'isTableCellInEditModeComponentFamilyState__{"familyKey":{"column":5,"row":1},"scopeId":"yourScopeId"}', }, true, ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext.ts index 40e3e30c2d..1665e2b960 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext.ts @@ -1,11 +1,11 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; import { ColumnDefinition } from '../../types/ColumnDefinition'; // TODO: separate scope contexts from event contexts -type RecordTableScopeInternalContextProps = ComponentStateKey & { +type RecordTableScopeInternalContextProps = RecoilComponentStateKey & { onColumnsChange: (columns: ColumnDefinition[]) => void; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2.ts b/packages/twenty-front/src/modules/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2.ts index f8b6716a86..9cd066b84e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2.ts @@ -1,8 +1,8 @@ import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { createComponentStateV2_alpha } from '@/ui/utilities/state/component-state/utils/createComponentStateV2_alpha'; export const hasRecordTableFetchedAllRecordsComponentStateV2 = - createComponentStateV2({ + createComponentStateV2_alpha({ key: 'hasRecordTableFetchedAllRecordsComponentStateV2', componentContext: RecordTableScopeInternalContext, defaultValue: false, diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts index 6f26d8a608..b51a7f63f4 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledLeftComponentState.ts @@ -1,8 +1,8 @@ import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { createComponentStateV2_alpha } from '@/ui/utilities/state/component-state/utils/createComponentStateV2_alpha'; export const isRecordTableScrolledLeftComponentState = - createComponentStateV2({ + createComponentStateV2_alpha({ key: 'isRecordTableScrolledLeftComponentState', componentContext: RecordTableScopeInternalContext, defaultValue: true, diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledTopComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledTopComponentState.ts index 564c567a60..5a206e88b7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledTopComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/isRecordTableScrolledTopComponentState.ts @@ -1,8 +1,8 @@ import { RecordTableScopeInternalContext } from '@/object-record/record-table/scopes/scope-internal-context/RecordTableScopeInternalContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { createComponentStateV2_alpha } from '@/ui/utilities/state/component-state/utils/createComponentStateV2_alpha'; export const isRecordTableScrolledTopComponentState = - createComponentStateV2({ + createComponentStateV2_alpha({ key: 'isRecordTableScrolledTopComponentState', componentContext: RecordTableScopeInternalContext, defaultValue: true, diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts index 3dbbfc7eff..eda26d3f1f 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type RelationPickerScopeInternalContextProps = ComponentStateKey; +type RelationPickerScopeInternalContextProps = RecoilComponentStateKey; export const RelationPickerScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainer.tsx b/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainer.tsx index da43d1ee0c..1ce2f5affe 100644 --- a/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainer.tsx +++ b/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainer.tsx @@ -4,6 +4,7 @@ import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers'; import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect'; import { ViewBar } from '@/views/components/ViewBar'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewType } from '@/views/types/ViewType'; const StyledContainer = styled.div` @@ -21,28 +22,30 @@ export const SignInBackgroundMockContainer = () => { return ( - {}} - optionsDropdownButton={ - - } - /> - - {}} - /> + + {}} + optionsDropdownButton={ + + } + /> + + {}} + /> + ); }; diff --git a/packages/twenty-front/src/modules/ui/feedback/dialog-manager/scopes/scope-internal-context/DialogManagerScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/scopes/scope-internal-context/DialogManagerScopeInternalContext.ts index fb6183db81..8620b0d28d 100644 --- a/packages/twenty-front/src/modules/ui/feedback/dialog-manager/scopes/scope-internal-context/DialogManagerScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/feedback/dialog-manager/scopes/scope-internal-context/DialogManagerScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type DialogManagerScopeInternalContextProps = ComponentStateKey; +type DialogManagerScopeInternalContextProps = RecoilComponentStateKey; export const DialogManagerScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext.ts index 75b7e1e135..c9686c2d2e 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/scopes/scope-internal-context/SnackBarManagerScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type SnackBarManagerScopeInternalContextProps = ComponentStateKey; +type SnackBarManagerScopeInternalContextProps = RecoilComponentStateKey; export const SnackBarManagerScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts index 0d117b23f2..ed9516be68 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type DropdownScopeInternalContextProps = ComponentStateKey; +type DropdownScopeInternalContextProps = RecoilComponentStateKey; export const DropdownScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/scopes/scope-internal-context/SelectableListScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/scopes/scope-internal-context/SelectableListScopeInternalContext.ts index c347ce3f05..b28f36103a 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/scopes/scope-internal-context/SelectableListScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/scopes/scope-internal-context/SelectableListScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type SelectableListScopeInternalContextProps = ComponentStateKey; +type SelectableListScopeInternalContextProps = RecoilComponentStateKey; export const SelectableListScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/ui/layout/tab/hooks/useTabList.ts b/packages/twenty-front/src/modules/ui/layout/tab/hooks/useTabList.ts index 9925dec4c6..0021c89fb7 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab/hooks/useTabList.ts +++ b/packages/twenty-front/src/modules/ui/layout/tab/hooks/useTabList.ts @@ -4,7 +4,7 @@ import { useTabListStates } from '@/ui/layout/tab/hooks/internal/useTabListState export const useTabList = (tabListId?: string) => { const { activeTabIdState } = useTabListStates({ - tabListScopeId: `${tabListId}-scope`, + tabListScopeId: tabListId, }); const setActiveTabId = useSetRecoilState(activeTabIdState); diff --git a/packages/twenty-front/src/modules/ui/layout/tab/scopes/scope-internal-context/TabListScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/layout/tab/scopes/scope-internal-context/TabListScopeInternalContext.ts index 878955256f..5212b7bdba 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab/scopes/scope-internal-context/TabListScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/layout/tab/scopes/scope-internal-context/TabListScopeInternalContext.ts @@ -1,7 +1,7 @@ import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type TabListScopeInternalContextProps = ComponentStateKey; +type TabListScopeInternalContextProps = RecoilComponentStateKey; export const TabListScopeInternalContext = createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts index 952fbfb0ee..cf3b747450 100644 --- a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts +++ b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts @@ -1,9 +1,9 @@ import { RecoilState, useRecoilState } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export const useRecoilScopedStateV2 = ( - recoilState: (scopedKey: ComponentStateKey) => RecoilState, + recoilState: (scopedKey: RecoilComponentStateKey) => RecoilState, scopeId: string, ) => { return useRecoilState( diff --git a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts index 78911a1abc..abf1b33900 100644 --- a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts +++ b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts @@ -1,10 +1,11 @@ import { Context, createContext } from 'react'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; -type ScopeInternalContext = Context; +type ScopeInternalContext = + Context; -export const createScopeInternalContext = ( +export const createScopeInternalContext = ( initialValue?: T, ) => { return createContext( diff --git a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedSelector.ts b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedSelector.ts index 8e92014b55..5f680bbda0 100644 --- a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedSelector.ts @@ -1,7 +1,7 @@ import { RecoilValueReadOnly } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export type RecoilScopedSelector = ( - scopedKey: ComponentStateKey, + scopedKey: RecoilComponentStateKey, ) => RecoilValueReadOnly; diff --git a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedState.ts b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedState.ts index beb36d9e99..44ed24a238 100644 --- a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/types/RecoilScopedState.ts @@ -1,7 +1,7 @@ import { RecoilState } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export type RecoilScopedState = ( - scopedKey: ComponentStateKey, + scopedKey: RecoilComponentStateKey, ) => RecoilState; diff --git a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId.ts b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId.ts index feb67c86db..bc5ce6e96b 100644 --- a/packages/twenty-front/src/modules/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId.ts +++ b/packages/twenty-front/src/modules/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId.ts @@ -1,2 +1,2 @@ export const getScopeIdFromComponentId = (componentId: string) => - `${componentId}-scope`; + `${componentId}`; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow.ts new file mode 100644 index 0000000000..8a497e3c3a --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow.ts @@ -0,0 +1,24 @@ +import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { isNonEmptyString } from '@sniptt/guards'; + +export const useAvailableComponentInstanceIdOrThrow = < + T extends { instanceId: string }, +>( + Context: ComponentInstanceStateContext, + instanceIdFromProps?: string, +): string => { + const instanceStateContext = useComponentInstanceStateContext(Context); + + const instanceIdFromContext = instanceStateContext?.instanceId; + + if (isNonEmptyString(instanceIdFromProps)) { + return instanceIdFromProps; + } else if (isNonEmptyString(instanceIdFromContext)) { + return instanceIdFromContext; + } else { + throw new Error( + 'Instance id is not provided and cannot be found in context.', + ); + } +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext.ts new file mode 100644 index 0000000000..17dbbf158c --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext.ts @@ -0,0 +1,12 @@ +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { useContext } from 'react'; + +export const useComponentInstanceStateContext = < + T extends { instanceId: string }, +>( + Context: ComponentInstanceStateContext, +) => { + const context = useContext(Context); + + return context; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilCallbackState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilCallbackState.ts new file mode 100644 index 0000000000..bf1143c06a --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilCallbackState.ts @@ -0,0 +1,27 @@ +import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; + +export const useRecoilCallbackState = ( + componentState: RecoilComponentState, + componentId?: string, +) => { + const componentContext = (window as any).componentContextStateMap?.get( + componentState.key, + ); + + if (!componentContext) { + throw new Error( + `Component context for key "${componentState.key}" is not defined`, + ); + } + + const internalScopeId = useAvailableScopeIdOrThrow( + componentContext, + getScopeIdOrUndefinedFromComponentId(componentId), + ); + + return componentState.atomFamily({ + scopeId: internalScopeId, + }); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2.ts new file mode 100644 index 0000000000..ff3f6ab060 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2.ts @@ -0,0 +1,128 @@ +/* eslint-disable no-redeclare */ +/* eslint-disable prefer-arrow/prefer-arrow-functions */ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentFamilyReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2'; +import { ComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilySelectorV2'; +import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2'; +import { ComponentReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2'; +import { ComponentSelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentSelectorV2'; +import { ComponentStateV2 } from '@/ui/utilities/state/component-state/types/ComponentStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { RecoilState, RecoilValueReadOnly, SerializableParam } from 'recoil'; + +export function useRecoilComponentCallbackStateV2( + componentState: ComponentStateV2, + instanceIdFromProps?: string, +): RecoilState; +export function useRecoilComponentCallbackStateV2( + componentSelector: ComponentSelectorV2, + instanceIdFromProps?: string, +): RecoilState; +export function useRecoilComponentCallbackStateV2( + componentReadOnlySelector: ComponentReadOnlySelectorV2, + instanceIdFromProps?: string, +): RecoilValueReadOnly; +export function useRecoilComponentCallbackStateV2< + ValueType, + FamilyKey extends SerializableParam, +>( + componentFamilyState: ComponentFamilyStateV2, + instanceIdFromProps?: string, +): (familyKey: FamilyKey) => RecoilState; +export function useRecoilComponentCallbackStateV2< + ValueType, + FamilyKey extends SerializableParam, +>( + componentFamilySelector: ComponentFamilySelectorV2, + instanceIdFromProps?: string, +): (familyKey: FamilyKey) => RecoilState; +export function useRecoilComponentCallbackStateV2< + ValueType, + FamilyKey extends SerializableParam, +>( + componentFamilyReadOnlySelector: ComponentFamilyReadOnlySelectorV2< + ValueType, + FamilyKey + >, + instanceIdFromProps?: string, +): (familyKey: FamilyKey) => RecoilValueReadOnly; +export function useRecoilComponentCallbackStateV2< + ValueType, + FamilyKey extends SerializableParam, +>( + componentFamilyState: ComponentFamilyStateV2, + instanceIdFromProps?: string, +): (familyKey: FamilyKey) => RecoilState; +export function useRecoilComponentCallbackStateV2< + ComponentState extends + | ComponentStateV2 + | ComponentSelectorV2 + | ComponentReadOnlySelectorV2 + | ComponentFamilyStateV2 + | ComponentFamilySelectorV2 + | ComponentFamilyReadOnlySelectorV2, + ValueType, + FamilyKey extends SerializableParam = never, +>( + componentState: ComponentState, + instanceIdFromProps?: string, +): + | RecoilState + | RecoilValueReadOnly + | ((familyKey: FamilyKey) => RecoilState) + | ((familyKey: FamilyKey) => RecoilValueReadOnly) { + const componentStateKey = componentState.key; + + const componentInstanceContext = + globalComponentInstanceContextMap.get(componentStateKey); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentStateKey}" is not defined, check the component state declaration.`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + switch (componentState.type) { + case 'ComponentState': { + return componentState.atomFamily({ + instanceId, + }); + } + case 'ComponentSelector': { + return componentState.selectorFamily({ + instanceId, + }); + } + case 'ComponentReadOnlySelector': { + return componentState.selectorFamily({ + instanceId, + }); + } + case 'ComponentFamilyState': { + return (familyKey: FamilyKey) => + componentState.atomFamily({ + instanceId, + familyKey, + }); + } + case 'ComponentFamilySelector': { + return (familyKey: FamilyKey) => + componentState.selectorFamily({ + instanceId, + familyKey, + }); + } + case 'ComponentFamilyReadOnlySelector': { + return (familyKey: FamilyKey) => + componentState.selectorFamily({ + instanceId, + familyKey, + }); + } + } +} diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2.ts new file mode 100644 index 0000000000..4e8edaa4e9 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2.ts @@ -0,0 +1,52 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentFamilyReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2'; +import { ComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilySelectorV2'; +import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SerializableParam, useRecoilValue } from 'recoil'; + +export const useRecoilComponentFamilyValueV2 = < + StateType, + FamilyKey extends SerializableParam, +>( + componentStateV2: + | ComponentFamilyStateV2 + | ComponentFamilySelectorV2 + | ComponentFamilyReadOnlySelectorV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +): StateType => { + const instanceContext = globalComponentInstanceContextMap.get( + componentStateV2.key, + ); + + if (!instanceContext) { + throw new Error( + `Instance context for key "${componentStateV2.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + instanceContext, + instanceIdFromProps, + ); + + switch (componentStateV2.type) { + case 'ComponentFamilyState': { + return useRecoilValue( + componentStateV2.atomFamily({ familyKey, instanceId }), + ); + } + case 'ComponentFamilySelector': { + return useRecoilValue( + componentStateV2.selectorFamily({ familyKey, instanceId }), + ); + } + case 'ComponentFamilyReadOnlySelector': { + return useRecoilValue( + componentStateV2.selectorFamily({ familyKey, instanceId }), + ); + } + } +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentState.ts new file mode 100644 index 0000000000..c0bd5e9d53 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentState.ts @@ -0,0 +1,28 @@ +import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; +import { useRecoilState } from 'recoil'; + +export const useRecoilComponentState = ( + componentState: RecoilComponentState, + componentId?: string, +) => { + const componentContext = (window as any).componentContextStateMap?.get( + componentState.key, + ); + + if (!componentContext) { + throw new Error( + `Component context for key "${componentState.key}" is not defined`, + ); + } + + const internalComponentId = useAvailableScopeIdOrThrow( + componentContext, + getScopeIdOrUndefinedFromComponentId(componentId), + ); + + return useRecoilState( + componentState.atomFamily({ scopeId: internalComponentId }), + ); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2.ts new file mode 100644 index 0000000000..a3d16acd4e --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2.ts @@ -0,0 +1,26 @@ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentStateV2 } from '@/ui/utilities/state/component-state/types/ComponentStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { useRecoilState } from 'recoil'; + +export const useRecoilComponentStateV2 = ( + componentState: ComponentStateV2, + instanceIdFromProps?: string, +) => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useRecoilState(componentState.atomFamily({ instanceId })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValue.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValue.ts index d72ee3cae3..efaa56de1a 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValue.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValue.ts @@ -2,10 +2,10 @@ import { useRecoilValue } from 'recoil'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; -import { ComponentState } from '@/ui/utilities/state/component-state/types/ComponentState'; +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; export const useRecoilComponentValue = ( - componentState: ComponentState, + componentState: RecoilComponentState, componentId?: string, ) => { const componentContext = (window as any).componentContextStateMap?.get( diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2.ts new file mode 100644 index 0000000000..a76b91c2eb --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2.ts @@ -0,0 +1,26 @@ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentStateV2 } from '@/ui/utilities/state/component-state/types/ComponentStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { useRecoilValue } from 'recoil'; + +export const useRecoilComponentValueV2 = ( + componentStateV2: ComponentStateV2, + instanceIdFromProps?: string, +) => { + const instanceContext = globalComponentInstanceContextMap.get( + componentStateV2.key, + ); + + if (!instanceContext) { + throw new Error( + `Instance context for key "${componentStateV2.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + instanceContext, + instanceIdFromProps, + ); + + return useRecoilValue(componentStateV2.atomFamily({ instanceId })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useScopeIdFromStateContext.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useScopeIdFromStateContext.ts index e500060c98..fa30c4cd53 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useScopeIdFromStateContext.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useScopeIdFromStateContext.ts @@ -1,9 +1,9 @@ import { useScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContext'; import { ComponentFamilyState } from '@/ui/utilities/state/component-state/types/ComponentFamilyState'; -import { ComponentState } from '@/ui/utilities/state/component-state/types/ComponentState'; +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; export const useScopeIdFromStateContext = ( - componentState: ComponentState | ComponentFamilyState, + componentState: RecoilComponentState | ComponentFamilyState, ) => { const componentContext = (window as any).componentContextStateMap?.get( componentState.key, diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2.ts new file mode 100644 index 0000000000..1ccf392a58 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2.ts @@ -0,0 +1,52 @@ +/* eslint-disable react-hooks/rules-of-hooks */ + +// We're disabling rules-of-hooks because we're sure that the call order cannot be modified +// because a component state cannot change its type during its lifecycle + +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilySelectorV2'; +import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SerializableParam, SetterOrUpdater, useSetRecoilState } from 'recoil'; + +export const useSetRecoilComponentFamilyStateV2 = < + StateType, + FamilyKey extends SerializableParam, +>( + componentState: + | ComponentFamilyStateV2 + | ComponentFamilySelectorV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +): SetterOrUpdater => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + switch (componentState.type) { + case 'ComponentFamilyState': { + return useSetRecoilState( + componentState.atomFamily({ familyKey, instanceId }), + ); + } + case 'ComponentFamilySelector': { + return useSetRecoilState( + componentState.selectorFamily({ + familyKey, + instanceId, + }), + ); + } + } +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentState.ts index 938d699b3f..9e1ca49a41 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentState.ts @@ -2,10 +2,10 @@ import { useSetRecoilState } from 'recoil'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; -import { ComponentState } from '@/ui/utilities/state/component-state/types/ComponentState'; +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; export const useSetRecoilComponentState = ( - componentState: ComponentState, + componentState: RecoilComponentState, componentId?: string, ) => { const componentContext = (window as any).componentContextStateMap?.get( diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2.ts new file mode 100644 index 0000000000..29e5f1914a --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2.ts @@ -0,0 +1,26 @@ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentStateV2 } from '@/ui/utilities/state/component-state/types/ComponentStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SetterOrUpdater, useSetRecoilState } from 'recoil'; + +export const useSetRecoilComponentStateV2 = ( + componentState: ComponentStateV2, + instanceIdFromProps?: string, +): SetterOrUpdater => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useSetRecoilState(componentState.atomFamily({ instanceId })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2.ts new file mode 100644 index 0000000000..0a01f598b7 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2.ts @@ -0,0 +1,14 @@ +import { ComponentFamilyStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilValueReadOnly, SerializableParam } from 'recoil'; + +export type ComponentFamilyReadOnlySelectorV2< + StateType, + FamilyKey extends SerializableParam, +> = { + type: Extract; + key: string; + selectorFamily: ( + componentFamilyStateKey: ComponentFamilyStateKeyV2, + ) => RecoilValueReadOnly; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilySelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilySelectorV2.ts new file mode 100644 index 0000000000..9ac6c58795 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilySelectorV2.ts @@ -0,0 +1,14 @@ +import { ComponentFamilyStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilState, SerializableParam } from 'recoil'; + +export type ComponentFamilySelectorV2< + StateType, + FamilyKey extends SerializableParam, +> = { + type: Extract; + key: string; + selectorFamily: ( + componentFamilyStateKey: ComponentFamilyStateKeyV2, + ) => RecoilState; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2.ts new file mode 100644 index 0000000000..a5e278b46c --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2.ts @@ -0,0 +1,7 @@ +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { SerializableParam } from 'recoil'; + +export type ComponentFamilyStateKeyV2 = + ComponentStateKeyV2 & { + familyKey: FamilyKey; + }; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateV2.ts new file mode 100644 index 0000000000..0410896ad9 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentFamilyStateV2.ts @@ -0,0 +1,14 @@ +import { ComponentFamilyStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilState, SerializableParam } from 'recoil'; + +export type ComponentFamilyStateV2< + StateType, + FamilyKey extends SerializableParam, +> = { + type: Extract; + key: string; + atomFamily: ( + componentFamilyStateKey: ComponentFamilyStateKeyV2, + ) => RecoilState; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentInstanceStateContext.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentInstanceStateContext.ts new file mode 100644 index 0000000000..10f558926b --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentInstanceStateContext.ts @@ -0,0 +1,4 @@ +import { Context } from 'react'; + +export type ComponentInstanceStateContext = + Context; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2.ts new file mode 100644 index 0000000000..9e62e0a484 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2.ts @@ -0,0 +1,11 @@ +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilValueReadOnly } from 'recoil'; + +export type ComponentReadOnlySelectorV2 = { + type: Extract; + key: string; + selectorFamily: ( + componentStateKey: ComponentStateKeyV2, + ) => RecoilValueReadOnly; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentSelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentSelectorV2.ts new file mode 100644 index 0000000000..f93ef3e222 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentSelectorV2.ts @@ -0,0 +1,11 @@ +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilState } from 'recoil'; + +export type ComponentSelectorV2 = { + type: Extract; + key: string; + selectorFamily: ( + componentStateKey: ComponentStateKeyV2, + ) => RecoilState; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentState.ts deleted file mode 100644 index 8fc43e8a29..0000000000 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RecoilState } from 'recoil'; - -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; - -export type ComponentState = { - key: string; - atomFamily: (componentStateKey: ComponentStateKey) => RecoilState; -}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKey.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKey.ts deleted file mode 100644 index fb69c4c122..0000000000 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKey.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type ComponentStateKey = { - scopeId: string; -}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKeyV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKeyV2.ts new file mode 100644 index 0000000000..524b9dfc88 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateKeyV2.ts @@ -0,0 +1,3 @@ +export type ComponentStateKeyV2 = { + instanceId: string; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateTypeV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateTypeV2.ts new file mode 100644 index 0000000000..83a3182a6a --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateTypeV2.ts @@ -0,0 +1,7 @@ +export type ComponentStateTypeV2 = + | 'ComponentState' + | 'ComponentFamilyState' + | 'ComponentSelector' + | 'ComponentReadOnlySelector' + | 'ComponentFamilySelector' + | 'ComponentFamilyReadOnlySelector'; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateV2.ts new file mode 100644 index 0000000000..faeab37dd2 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/ComponentStateV2.ts @@ -0,0 +1,11 @@ +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { ComponentStateTypeV2 } from '@/ui/utilities/state/component-state/types/ComponentStateTypeV2'; +import { RecoilState } from 'recoil'; + +export type ComponentStateV2 = { + type: Extract; + key: string; + atomFamily: ( + componentStateKey: ComponentStateKeyV2, + ) => RecoilState; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentState.ts new file mode 100644 index 0000000000..8c007e5bdd --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentState.ts @@ -0,0 +1,9 @@ +import { RecoilState } from 'recoil'; +import { RecoilComponentStateKey } from './RecoilComponentStateKey'; + +export type RecoilComponentState = { + key: string; + atomFamily: ( + componentStateKey: RecoilComponentStateKey, + ) => RecoilState; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentStateKey.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentStateKey.ts new file mode 100644 index 0000000000..6627f39f92 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/types/RecoilComponentStateKey.ts @@ -0,0 +1,3 @@ +export type RecoilComponentStateKey = { + scopeId: string; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2.ts new file mode 100644 index 0000000000..04c9a2d2a8 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2.ts @@ -0,0 +1,58 @@ +import { selectorFamily, SerializableParam } from 'recoil'; + +import { ComponentFamilyReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2'; +import { ComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilySelectorV2'; +import { ComponentFamilyStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2'; +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SelectorGetter } from '@/ui/utilities/state/types/SelectorGetter'; +import { SelectorSetter } from '@/ui/utilities/state/types/SelectorSetter'; +import { isDefined } from 'twenty-ui'; + +export const createComponentFamilySelectorV2 = < + ValueType, + FamilyKey extends SerializableParam, +>({ + key, + get, + set, + componentInstanceContext, +}: { + key: string; + get: SelectorGetter>; + set?: SelectorSetter>; + componentInstanceContext: ComponentInstanceStateContext | null; +}): + | ComponentFamilySelectorV2 + | ComponentFamilyReadOnlySelectorV2 => { + if (isDefined(componentInstanceContext)) { + globalComponentInstanceContextMap.set(key, componentInstanceContext); + } + + if (isDefined(set)) { + return { + type: 'ComponentFamilySelector', + key, + selectorFamily: selectorFamily< + ValueType, + ComponentFamilyStateKeyV2 + >({ + key, + get, + set, + }), + } satisfies ComponentFamilySelectorV2; + } else { + return { + type: 'ComponentFamilyReadOnlySelector', + key, + selectorFamily: selectorFamily< + ValueType, + ComponentFamilyStateKeyV2 + >({ + key, + get, + }), + } satisfies ComponentFamilyReadOnlySelectorV2; + } +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilyStateV2.ts index b3f7b3b5b4..2d1f42e61f 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilyStateV2.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentFamilyStateV2.ts @@ -1,13 +1,15 @@ +import { ComponentFamilyStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKeyV2'; +import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2'; +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; import { AtomEffect, atomFamily, SerializableParam } from 'recoil'; -import { ScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext'; -import { ComponentFamilyStateKey } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateKey'; import { isDefined } from 'twenty-ui'; -type CreateComponentFamilyStateV2Type = { +type CreateComponentFamilyStateArgs = { key: string; defaultValue: ValueType; - componentContext: ScopeInternalContext | null; + componentInstanceContext: ComponentInstanceStateContext | null; effects?: AtomEffect[]; }; @@ -18,22 +20,22 @@ export const createComponentFamilyStateV2 = < key, effects, defaultValue, - componentContext, -}: CreateComponentFamilyStateV2Type) => { - if (isDefined(componentContext)) { - if (!isDefined((window as any).componentContextStateMap)) { - (window as any).componentContextStateMap = new Map(); - } - - (window as any).componentContextStateMap.set(key, componentContext); + componentInstanceContext, +}: CreateComponentFamilyStateArgs): ComponentFamilyStateV2< + ValueType, + FamilyKey +> => { + if (isDefined(componentInstanceContext)) { + globalComponentInstanceContextMap.set(key, componentInstanceContext); } return { + type: 'ComponentFamilyState', key, - atomFamily: atomFamily>({ + atomFamily: atomFamily>({ key, default: defaultValue, effects, }), - }; + } satisfies ComponentFamilyStateV2; }; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentInstanceContext.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentInstanceContext.ts new file mode 100644 index 0000000000..68b9d93a66 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentInstanceContext.ts @@ -0,0 +1,13 @@ +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { createContext } from 'react'; + +export const createComponentInstanceContext = < + T extends ComponentStateKeyV2 = ComponentStateKeyV2, +>( + initialValue?: T, +) => { + return createContext( + initialValue ?? null, + ) as ComponentInstanceStateContext; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentReadOnlySelector.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentReadOnlySelector.ts index 3369abc774..5fa3378239 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentReadOnlySelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentReadOnlySelector.ts @@ -1,6 +1,6 @@ import { selectorFamily } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; import { SelectorGetter } from '@/ui/utilities/state/types/SelectorGetter'; export const createComponentReadOnlySelector = ({ @@ -8,9 +8,9 @@ export const createComponentReadOnlySelector = ({ get, }: { key: string; - get: SelectorGetter; + get: SelectorGetter; }) => { - return selectorFamily({ + return selectorFamily({ key, get, }); diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelector.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelector.ts index c4dfc647bb..545845ccda 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelector.ts @@ -1,6 +1,6 @@ import { selectorFamily } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; import { SelectorGetter } from '@/ui/utilities/state/types/SelectorGetter'; import { SelectorSetter } from '@/ui/utilities/state/types/SelectorSetter'; @@ -10,10 +10,10 @@ export const createComponentSelector = ({ set, }: { key: string; - get: SelectorGetter; - set: SelectorSetter; + get: SelectorGetter; + set: SelectorSetter; }) => { - return selectorFamily({ + return selectorFamily({ key, get, set, diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts new file mode 100644 index 0000000000..352bff92ff --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts @@ -0,0 +1,47 @@ +import { selectorFamily } from 'recoil'; + +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { ComponentReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentReadOnlySelectorV2'; +import { ComponentSelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentSelectorV2'; +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SelectorGetter } from '@/ui/utilities/state/types/SelectorGetter'; +import { SelectorSetter } from '@/ui/utilities/state/types/SelectorSetter'; +import { isDefined } from 'twenty-ui'; + +export const createComponentSelectorV2 = ({ + key, + get, + set, + instanceContext, +}: { + key: string; + get: SelectorGetter; + set?: SelectorSetter; + instanceContext: ComponentInstanceStateContext | null; +}): ComponentSelectorV2 | ComponentReadOnlySelectorV2 => { + if (isDefined(instanceContext)) { + globalComponentInstanceContextMap.set(key, instanceContext); + } + + if (isDefined(set)) { + return { + type: 'ComponentSelector', + key, + selectorFamily: selectorFamily({ + key, + get, + set, + }), + } satisfies ComponentSelectorV2; + } else { + return { + type: 'ComponentReadOnlySelector', + key, + selectorFamily: selectorFamily({ + key, + get, + }), + } satisfies ComponentReadOnlySelectorV2; + } +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentState.ts index ffbd9dd7eb..5f2cd0badc 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentState.ts @@ -1,6 +1,6 @@ import { AtomEffect, atomFamily } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; type CreateComponentStateType = { key: string; @@ -13,7 +13,7 @@ export const createComponentState = ({ defaultValue, effects, }: CreateComponentStateType) => { - return atomFamily({ + return atomFamily({ key, default: defaultValue, effects: effects, diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2.ts index 1fda1db821..dc87cbf0cf 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2.ts @@ -1,37 +1,35 @@ +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2'; +import { ComponentStateV2 } from '@/ui/utilities/state/component-state/types/ComponentStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; import { AtomEffect, atomFamily } from 'recoil'; -import { ScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext'; -import { ComponentState } from '@/ui/utilities/state/component-state/types/ComponentState'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; import { isDefined } from '~/utils/isDefined'; -type CreateComponentStateV2Type = { +type CreateComponentInstanceStateArgs = { key: string; defaultValue: ValueType; - componentContext?: ScopeInternalContext | null; + componentInstanceContext: ComponentInstanceStateContext | null; effects?: AtomEffect[]; }; export const createComponentStateV2 = ({ key, defaultValue, - componentContext, + componentInstanceContext, effects, -}: CreateComponentStateV2Type): ComponentState => { - if (isDefined(componentContext)) { - if (!isDefined((window as any).componentContextStateMap)) { - (window as any).componentContextStateMap = new Map(); - } - - (window as any).componentContextStateMap.set(key, componentContext); +}: CreateComponentInstanceStateArgs): ComponentStateV2 => { + if (isDefined(componentInstanceContext)) { + globalComponentInstanceContextMap.set(key, componentInstanceContext); } return { + type: 'ComponentState', key, - atomFamily: atomFamily({ + atomFamily: atomFamily({ key, default: defaultValue, effects: effects, }), - }; + } satisfies ComponentStateV2; }; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2_alpha.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2_alpha.ts new file mode 100644 index 0000000000..2533b05427 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentStateV2_alpha.ts @@ -0,0 +1,38 @@ +import { AtomEffect, atomFamily } from 'recoil'; + +import { ScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext'; + +import { RecoilComponentState } from '@/ui/utilities/state/component-state/types/RecoilComponentState'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; +import { isDefined } from '~/utils/isDefined'; + +type CreateComponentStateV2Type = { + key: string; + defaultValue: ValueType; + componentContext?: ScopeInternalContext | null; + effects?: AtomEffect[]; +}; + +export const createComponentStateV2_alpha = ({ + key, + defaultValue, + componentContext, + effects, +}: CreateComponentStateV2Type): RecoilComponentState => { + if (isDefined(componentContext)) { + if (!isDefined((window as any).componentContextStateMap)) { + (window as any).componentContextStateMap = new Map(); + } + + (window as any).componentContextStateMap.set(key, componentContext); + } + + return { + key, + atomFamily: atomFamily({ + key, + default: defaultValue, + effects: effects, + }), + }; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createEventContext.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createEventContext.ts new file mode 100644 index 0000000000..723e1c3f3e --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createEventContext.ts @@ -0,0 +1,9 @@ +import { createContext } from 'react'; + +export const createEventContext = < + Events extends Record void>, +>( + initialValue?: Events, +) => { + return createContext((initialValue ?? {}) as Events); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector.ts index 98489a2669..da5b494b6e 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector.ts @@ -1,10 +1,10 @@ import { RecoilValueReadOnly } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export const extractComponentReadOnlySelector = ( componentSelector: ( - componentStateKey: ComponentStateKey, + componentStateKey: RecoilComponentStateKey, ) => RecoilValueReadOnly, scopeId: string, ) => { diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentSelector.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentSelector.ts index 60f6fa75da..40befcf6fe 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentSelector.ts @@ -1,10 +1,10 @@ import { RecoilState } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export const extractComponentSelector = ( componentSelector: ( - componentStateKey: ComponentStateKey, + componentStateKey: RecoilComponentStateKey, ) => RecoilState, scopeId: string, ) => { diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentState.ts index ea797fe77e..cac495c0c2 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/extractComponentState.ts @@ -1,10 +1,10 @@ import { RecoilState } from 'recoil'; -import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; +import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; export const extractComponentState = ( componentState: ( - componentStateKey: ComponentStateKey, + componentStateKey: RecoilComponentStateKey, ) => RecoilState, scopeId: string, ) => { diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap.ts new file mode 100644 index 0000000000..3dd5ce612f --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap.ts @@ -0,0 +1,21 @@ +import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { isDefined } from 'twenty-ui'; + +class ComponentInstanceContextMap { + constructor() { + if (!isDefined((window as any).componentComponentStateContextMap)) { + (window as any).componentComponentStateContextMap = new Map(); + } + } + + public get(key: string): ComponentInstanceStateContext { + return (window as any).componentComponentStateContextMap.get(key); + } + + public set(key: string, context: ComponentInstanceStateContext) { + (window as any).componentComponentStateContextMap.set(key, context); + } +} + +export const globalComponentInstanceContextMap = + new ComponentInstanceContextMap(); diff --git a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx index 23613d5c35..bbaa9af31b 100644 --- a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; import { MultipleFiltersDropdownContent } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent'; import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown'; @@ -8,8 +7,11 @@ import { FilterOperand } from '@/object-record/object-filter-dropdown/types/Filt import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { EditableFilterChip } from '@/views/components/EditableFilterChip'; -import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters'; + +import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters'; +import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { isDefined } from '~/utils/isDefined'; type EditableFilterDropdownButtonProps = { @@ -24,7 +26,6 @@ export const EditableFilterDropdownButton = ({ hotkeyScope, }: EditableFilterDropdownButtonProps) => { const { - availableFilterDefinitionsState, setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, setSelectedFilter, @@ -32,13 +33,15 @@ export const EditableFilterDropdownButton = ({ filterDropdownId: viewFilterDropdownId, }); - const availableFilterDefinitions = useRecoilValue( - availableFilterDefinitionsState, + // TODO: verify this instance id works + const availableFilterDefinitions = useRecoilComponentValueV2( + availableFilterDefinitionsComponentState, + viewFilterDropdownId, ); const { closeDropdown } = useDropdown(viewFilterDropdownId); - const { removeCombinedViewFilter } = useCombinedViewFilters(); + const { deleteCombinedViewFilter } = useDeleteCombinedViewFilters(); useEffect(() => { const filterDefinition = availableFilterDefinitions.find( @@ -63,7 +66,7 @@ export const EditableFilterDropdownButton = ({ const handleRemove = () => { closeDropdown(); - removeCombinedViewFilter(viewFilter.id); + deleteCombinedViewFilter(viewFilter.id); }; const handleDropdownClickOutside = useCallback(() => { @@ -72,9 +75,9 @@ export const EditableFilterDropdownButton = ({ !value && ![FilterOperand.IsEmpty, FilterOperand.IsNotEmpty].includes(operand) ) { - removeCombinedViewFilter(fieldId); + deleteCombinedViewFilter(fieldId); } - }, [viewFilter, removeCombinedViewFilter]); + }, [viewFilter, deleteCombinedViewFilter]); return ( { - const { removeCombinedViewSort, upsertCombinedViewSort } = - useCombinedViewSorts(); + const { deleteCombinedViewSort } = useDeleteCombinedViewSorts(); + + const { upsertCombinedViewSort } = useUpsertCombinedViewSorts(); const handleRemoveClick = () => { - removeCombinedViewSort(viewSort.fieldMetadataId); + deleteCombinedViewSort(viewSort.fieldMetadataId); }; const handleClick = () => { diff --git a/packages/twenty-front/src/modules/views/components/QueryParamsFiltersEffect.tsx b/packages/twenty-front/src/modules/views/components/QueryParamsFiltersEffect.tsx index 25278a77c9..10ecdfa83b 100644 --- a/packages/twenty-front/src/modules/views/components/QueryParamsFiltersEffect.tsx +++ b/packages/twenty-front/src/modules/views/components/QueryParamsFiltersEffect.tsx @@ -1,18 +1,23 @@ import { useEffect } from 'react'; -import { useSetRecoilState } from 'recoil'; +import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2'; import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; -import { useViewStates } from '@/views/hooks/internal/useViewStates'; -import { useResetCurrentView } from '@/views/hooks/useResetCurrentView'; +import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; +import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates'; +import { unsavedToUpsertViewFiltersComponentFamilyState } from '@/views/states/unsavedToUpsertViewFiltersComponentFamilyState'; +import { isDefined } from 'twenty-ui'; export const QueryParamsFiltersEffect = () => { - const { hasFiltersQueryParams, getFiltersFromQueryParams } = + const { hasFiltersQueryParams, getFiltersFromQueryParams, viewIdQueryParam } = useViewFromQueryParams(); - const { unsavedToUpsertViewFiltersState } = useViewStates(); - const setUnsavedViewFilter = useSetRecoilState( - unsavedToUpsertViewFiltersState, + + const setUnsavedViewFilter = useSetRecoilComponentFamilyStateV2( + unsavedToUpsertViewFiltersComponentFamilyState, + { viewId: viewIdQueryParam }, ); - const { resetCurrentView } = useResetCurrentView(); + + const { resetUnsavedViewStates } = useResetUnsavedViewStates(); + const { currentViewId } = useGetCurrentView(); useEffect(() => { if (!hasFiltersQueryParams) { @@ -26,13 +31,16 @@ export const QueryParamsFiltersEffect = () => { }); return () => { - resetCurrentView(); + if (isDefined(currentViewId)) { + resetUnsavedViewStates(currentViewId); + } }; }, [ getFiltersFromQueryParams, hasFiltersQueryParams, - resetCurrentView, + resetUnsavedViewStates, setUnsavedViewFilter, + currentViewId, ]); return <>; diff --git a/packages/twenty-front/src/modules/views/components/QueryParamsViewIdEffect.tsx b/packages/twenty-front/src/modules/views/components/QueryParamsViewIdEffect.tsx index 74d516a7f9..2ee24d43fd 100644 --- a/packages/twenty-front/src/modules/views/components/QueryParamsViewIdEffect.tsx +++ b/packages/twenty-front/src/modules/views/components/QueryParamsViewIdEffect.tsx @@ -1,22 +1,26 @@ import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem'; import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; -import { useViewStates } from '@/views/hooks/internal/useViewStates'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; -import { useResetCurrentView } from '@/views/hooks/useResetCurrentView'; +import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { isUndefined } from '@sniptt/guards'; import { useEffect } from 'react'; -import { useRecoilState } from 'recoil'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDefined } from '~/utils/isDefined'; export const QueryParamsViewIdEffect = () => { const { getFiltersFromQueryParams, viewIdQueryParam } = useViewFromQueryParams(); - const { currentViewIdState, componentId: objectNamePlural } = useViewStates(); - const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState); + // TODO: fix this implicit hack + const { instanceId: objectNamePlural } = useGetCurrentView(); + + const [currentViewId, setCurrentViewId] = useRecoilComponentStateV2( + currentViewIdComponentState, + ); + const { viewsOnCurrentObject } = useGetCurrentView(); const { findObjectMetadataItemByNamePlural } = useFilteredObjectMetadataItems(); @@ -34,13 +38,14 @@ export const QueryParamsViewIdEffect = () => { lastVisitedObjectMetadataItemId, ); - const { resetCurrentView } = useResetCurrentView(); + // // TODO: scope view bar per view id if possible + // const { resetCurrentView } = useResetCurrentView(); - useEffect(() => { - if (isDefined(currentViewId)) { - resetCurrentView(); - } - }, [resetCurrentView, currentViewId]); + // useEffect(() => { + // if (isDefined(currentViewId)) { + // resetCurrentView(); + // } + // }, [resetCurrentView, currentViewId]); useEffect(() => { const indexView = viewsOnCurrentObject.find((view) => view.key === 'INDEX'); diff --git a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx index b41269b4e7..fa40544755 100644 --- a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx +++ b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx @@ -1,6 +1,4 @@ -import { useCallback } from 'react'; import styled from '@emotion/styled'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; import { IconChevronDown, IconPlus } from 'twenty-ui'; import { Button } from '@/ui/input/button/components/Button'; @@ -10,14 +8,18 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { UPDATE_VIEW_BUTTON_DROPDOWN_ID } from '@/views/constants/UpdateViewButtonDropdownId'; import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; -import { useViewStates } from '@/views/hooks/internal/useViewStates'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useSaveCurrentViewFiltersAndSorts } from '@/views/hooks/useSaveCurrentViewFiltersAndSorts'; +import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; +import { canPersistViewComponentFamilySelector } from '@/views/states/selectors/canPersistViewComponentFamilySelector'; import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId'; import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode'; -import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates'; +import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState'; const StyledContainer = styled.div` border-radius: ${({ theme }) => theme.border.radius.md}; @@ -33,12 +35,16 @@ export type UpdateViewButtonGroupProps = { export const UpdateViewButtonGroup = ({ hotkeyScope, }: UpdateViewButtonGroupProps) => { - const { canPersistViewSelector, currentViewIdState } = useViewStates(); const { saveCurrentViewFilterAndSorts } = useSaveCurrentViewFiltersAndSorts(); const { setViewPickerMode } = useViewPickerMode(); - const { viewPickerReferenceViewIdState } = useViewPickerStates(); - const canPersistView = useRecoilValue(canPersistViewSelector()); + + const currentViewId = useRecoilComponentValueV2(currentViewIdComponentState); + + const canPersistView = useRecoilComponentFamilyValueV2( + canPersistViewComponentFamilySelector, + { viewId: currentViewId }, + ); const { closeDropdown: closeUpdateViewButtonDropdown } = useDropdown( UPDATE_VIEW_BUTTON_DROPDOWN_ID, @@ -48,30 +54,31 @@ export const UpdateViewButtonGroup = ({ ); const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); - const currentViewId = useRecoilValue(currentViewIdState); - - const setViewPickerReferenceViewId = useSetRecoilState( - viewPickerReferenceViewIdState, + const setViewPickerReferenceViewId = useSetRecoilComponentStateV2( + viewPickerReferenceViewIdComponentState, ); - const handleViewCreate = useCallback(() => { + const openViewPickerInCreateMode = () => { if (!currentViewId) { return; } + openViewPickerDropdown(); setViewPickerReferenceViewId(currentViewId); - setViewPickerMode('create'); + setViewPickerMode('create-from-current'); closeUpdateViewButtonDropdown(); - }, [ - closeUpdateViewButtonDropdown, - currentViewId, - openViewPickerDropdown, - setViewPickerMode, - setViewPickerReferenceViewId, - ]); + }; - const handleViewUpdate = async () => { + const handleCreateViewClick = () => { + openViewPickerInCreateMode(); + }; + + const handleSaveAsNewViewClick = () => { + openViewPickerInCreateMode(); + }; + + const handleUpdateViewClick = async () => { await saveCurrentViewFilterAndSorts(); }; @@ -87,7 +94,7 @@ export const UpdateViewButtonGroup = ({ {currentViewWithCombinedFiltersAndSorts?.key !== 'INDEX' ? ( -