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 3b5672783..9abc6b784 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 caf6db621..59ee04d92 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 7546dc51c..ddaaf2e6a 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 5f57a990d..ddfb5125b 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 c09981493..b97deda7b 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 91aed0b40..b69584cb3 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 8325af5e8..b09d33a38 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 82888840c..8d9e5f1d0 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 1c29844bd..be81417c1 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 6bf6ddf0d..64de66d89 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 4a797a259..2cbe44c58 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 44373f1fa..3a857e28c 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 b36788076..40eb5623e 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 8454813f3..35a7798bc 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 22aeae0ed..44ac1e08e 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 5c7f8fb2d..f3847113f 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 eebf6c22d..94769746b 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 e8cdaa20f..3af323792 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 efe7e4cb9..1a16e7fee 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 32ae2ffc5..428537f69 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 a72fda64b..2ea5bcdc4 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 18997fbad..41d4fc49d 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 a5253a49e..b285fe345 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 bbe862c0a..31dac4ae7 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 3d0ce75a0..bac46e652 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 9e1c2b28b..c3d5e87d9 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 f41025398..0b2c810bc 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 6954b6044..71eb045ab 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 1e7ec2088..23b7e3b00 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 40e3e30c2..1665e2b96 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 f8b6716a8..9cd066b84 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 6f26d8a60..b51a7f63f 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 564c567a6..5a206e88b 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 3dbbfc7ef..eda26d3f1 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 da43d1ee0..1ce2f5aff 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 fb6183db8..8620b0d28 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 75b7e1e13..c9686c2d2 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 0d117b23f..ed9516be6 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 c347ce3f0..b28f36103 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 9925dec4c..0021c89fb 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 878955256..5212b7bdb 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 952fbfb0e..cf3b74745 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 78911a1ab..abf1b3390 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 8e92014b5..5f680bbda 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 beb36d9e9..44ed24a23 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 feb67c86d..bc5ce6e96 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 000000000..8a497e3c3 --- /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 000000000..17dbbf158 --- /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 000000000..bf1143c06 --- /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 000000000..ff3f6ab06 --- /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 000000000..4e8edaa4e --- /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 000000000..c0bd5e9d5 --- /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 000000000..a3d16acd4 --- /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 d72ee3cae..efaa56de1 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 000000000..a76b91c2e --- /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 e500060c9..fa30c4cd5 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 000000000..1ccf392a5 --- /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 938d699b3..9e1ca49a4 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 000000000..29e5f1914 --- /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 000000000..0a01f598b --- /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 000000000..9ac6c5879 --- /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 000000000..a5e278b46 --- /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 000000000..0410896ad --- /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 000000000..10f558926 --- /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 000000000..9e62e0a48 --- /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 000000000..f93ef3e22 --- /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 8fc43e8a2..000000000 --- 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 fb69c4c12..000000000 --- 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 000000000..524b9dfc8 --- /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 000000000..83a3182a6 --- /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 000000000..faeab37dd --- /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 000000000..8c007e5bd --- /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 000000000..6627f39f9 --- /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 000000000..04c9a2d2a --- /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 b3f7b3b5b..2d1f42e61 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 000000000..68b9d93a6 --- /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 3369abc77..5fa337823 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 c4dfc647b..545845ccd 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 000000000..352bff92f --- /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 ffbd9dd7e..5f2cd0bad 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 1fda1db82..dc87cbf0c 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 000000000..2533b0542 --- /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 000000000..723e1c3f3 --- /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 98489a266..da5b494b6 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 60f6fa75d..40befcf6f 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 ea797fe77..cac495c0c 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 000000000..3dd5ce612 --- /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 23613d5c3..bbaa9af31 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 25278a77c..10ecdfa83 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 74d516a7f..2ee24d43f 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 b41269b4e..fa4054475 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' ? ( -