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 <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2024-09-20 16:13:29 +02:00
committed by GitHub
parent bebeb1515b
commit 25522752e4
177 changed files with 3132 additions and 1745 deletions

View File

@ -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 =

View File

@ -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]

View File

@ -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;
}

View File

@ -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);

View File

@ -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<typeof MultipleFiltersDropdownButton> = {
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<typeof MultipleFiltersDropdownButton> = {
},
]);
return (
<ObjectFilterDropdownScope filterScopeId="entity-tasks-filter-scope">
<Story />
</ObjectFilterDropdownScope>
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
<ObjectFilterDropdownScope filterScopeId={instanceId}>
<Story />
</ObjectFilterDropdownScope>
</ViewComponentInstanceContext.Provider>
);
},
ObjectMetadataItemsDecorator,

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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<ObjectFilterDropdownScopeInternalContextProps>();

View File

@ -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 }) => (
<RecoilRoot>{children}</RecoilRoot>
@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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;
};

View File

@ -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<FieldMetadata>[]) => void;
onColumnsChange: (column: RecordBoardColumnDefinition[]) => void;
};

View File

@ -27,7 +27,7 @@ export const useInitDraftValueV2 = <FieldValue>() => {
const recordFieldInputScopeId = `${getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
)}-scope`;
)}`;
const getDraftValueSelector = extractComponentSelector<
FieldInputDraftValue<FieldValue> | undefined

View File

@ -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<RecordFieldInputScopeInternalContextProps>();

View File

@ -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 (
<StyledContainer>
<InformationBannerWrapper />
<RecordFieldValueSelectorContextProvider>
<SpreadsheetImportProvider>
<StyledContainerWithPadding>
<ViewBar
viewBarId={recordIndexId}
optionsDropdownButton={
<RecordIndexOptionsDropdown
recordIndexId={recordIndexId}
objectNameSingular={objectNameSingular}
viewType={recordIndexViewType ?? ViewType.Table}
/>
}
onCurrentViewChange={(view) => {
if (!view) {
return;
<ViewComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
>
<RecordFieldValueSelectorContextProvider>
<SpreadsheetImportProvider>
<StyledContainerWithPadding>
<ViewBar
viewBarId={recordIndexId}
optionsDropdownButton={
<RecordIndexOptionsDropdown
recordIndexId={recordIndexId}
objectNameSingular={objectNameSingular}
viewType={recordIndexViewType ?? ViewType.Table}
/>
}
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);
}}
/>
<RecordIndexViewBarEffect
objectNamePlural={objectNamePlural}
viewBarId={recordIndexId}
/>
</StyledContainerWithPadding>
</SpreadsheetImportProvider>
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);
}}
/>
<RecordIndexViewBarEffect
objectNamePlural={objectNamePlural}
viewBarId={recordIndexId}
/>
</StyledContainerWithPadding>
</SpreadsheetImportProvider>
{recordIndexViewType === ViewType.Table && (
<>
<RecordIndexTableContainer
recordTableId={recordIndexId}
viewBarId={recordIndexId}
/>
<RecordIndexTableContainerEffect
objectNameSingular={objectNameSingular}
recordTableId={recordIndexId}
viewBarId={recordIndexId}
/>
</>
)}
{recordIndexViewType === ViewType.Kanban && (
<StyledContainerWithPadding fullHeight>
<RecordIndexBoardContainer
recordBoardId={recordIndexId}
viewBarId={recordIndexId}
objectNameSingular={objectNameSingular}
/>
<RecordIndexBoardDataLoader
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
<RecordIndexBoardDataLoaderEffect
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
</StyledContainerWithPadding>
)}
</RecordFieldValueSelectorContextProvider>
{recordIndexViewType === ViewType.Table && (
<>
<RecordIndexTableContainer
recordTableId={recordIndexId}
viewBarId={recordIndexId}
/>
<RecordIndexTableContainerEffect
objectNameSingular={objectNameSingular}
recordTableId={recordIndexId}
viewBarId={recordIndexId}
/>
</>
)}
{recordIndexViewType === ViewType.Kanban && (
<StyledContainerWithPadding fullHeight>
<RecordIndexBoardContainer
recordBoardId={recordIndexId}
viewBarId={recordIndexId}
objectNameSingular={objectNameSingular}
/>
<RecordIndexBoardDataLoader
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
<RecordIndexBoardDataLoaderEffect
objectNameSingular={objectNameSingular}
recordBoardId={recordIndexId}
/>
</StyledContainerWithPadding>
)}
</RecordFieldValueSelectorContextProvider>
</ViewComponentInstanceContext.Provider>
</StyledContainer>
);
};

View File

@ -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);
});
};

View File

@ -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);

View File

@ -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) => {

View File

@ -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(

View File

@ -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) => {

View File

@ -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(() => {

View File

@ -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 }) => (
<Router>
<RecoilRoot>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<SnackBarManagerScopeInternalContext.Provider
value={{
scopeId: 'snack-bar-manager',
}}
>
<Router>
<RecoilRoot>
<MockedProvider addTypename={false} mocks={mocks}>
{children}
</MockedProvider>
</SnackBarProviderScope>
</RecoilRoot>
</Router>
</RecoilRoot>
</Router>
</SnackBarManagerScopeInternalContext.Provider>
);
const graphqlEmptyResponse = [
@ -174,15 +178,19 @@ const graphqlEmptyResponse = [
];
const WrapperWithEmptyResponse = ({ children }: { children: ReactNode }) => (
<Router>
<RecoilRoot>
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<SnackBarManagerScopeInternalContext.Provider
value={{
scopeId: 'snack-bar-manager',
}}
>
<Router>
<RecoilRoot>
<MockedProvider addTypename={false} mocks={graphqlEmptyResponse}>
{children}
</MockedProvider>
</SnackBarProviderScope>
</RecoilRoot>
</Router>
</RecoilRoot>
</Router>
</SnackBarManagerScopeInternalContext.Provider>
);
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();
});
});

View File

@ -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(

View File

@ -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());

View File

@ -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' &&

View File

@ -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,
);

View File

@ -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<FieldMetadata>[]) => void;
};

View File

@ -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<boolean>({
createComponentStateV2_alpha<boolean>({
key: 'hasRecordTableFetchedAllRecordsComponentStateV2',
componentContext: RecordTableScopeInternalContext,
defaultValue: false,

View File

@ -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<boolean>({
createComponentStateV2_alpha<boolean>({
key: 'isRecordTableScrolledLeftComponentState',
componentContext: RecordTableScopeInternalContext,
defaultValue: true,

View File

@ -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<boolean>({
createComponentStateV2_alpha<boolean>({
key: 'isRecordTableScrolledTopComponentState',
componentContext: RecordTableScopeInternalContext,
defaultValue: true,

View File

@ -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<RelationPickerScopeInternalContextProps>();

View File

@ -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 (
<StyledContainer>
<ViewBar
viewBarId={viewBarId}
onCurrentViewChange={async () => {}}
optionsDropdownButton={
<RecordIndexOptionsDropdown
recordIndexId={recordIndexId}
objectNameSingular={objectNameSingular}
viewType={ViewType.Table}
/>
}
/>
<SignInBackgroundMockContainerEffect
objectNamePlural={objectNamePlural}
recordTableId={recordIndexId}
viewId={viewBarId}
/>
<RecordTableWithWrappers
objectNameSingular={objectNameSingular}
recordTableId={recordIndexId}
viewBarId={viewBarId}
updateRecordMutation={() => {}}
/>
<ViewComponentInstanceContext.Provider value={{ instanceId: viewBarId }}>
<ViewBar
viewBarId={viewBarId}
onCurrentViewChange={async () => {}}
optionsDropdownButton={
<RecordIndexOptionsDropdown
recordIndexId={recordIndexId}
objectNameSingular={objectNameSingular}
viewType={ViewType.Table}
/>
}
/>
<SignInBackgroundMockContainerEffect
objectNamePlural={objectNamePlural}
recordTableId={recordIndexId}
viewId={viewBarId}
/>
<RecordTableWithWrappers
objectNameSingular={objectNameSingular}
recordTableId={recordIndexId}
viewBarId={viewBarId}
updateRecordMutation={() => {}}
/>
</ViewComponentInstanceContext.Provider>
</StyledContainer>
);
};

View File

@ -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<DialogManagerScopeInternalContextProps>();

View File

@ -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<SnackBarManagerScopeInternalContextProps>();

View File

@ -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<DropdownScopeInternalContextProps>();

View File

@ -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<SelectableListScopeInternalContextProps>();

View File

@ -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);

View File

@ -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<TabListScopeInternalContextProps>();

View File

@ -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 = <StateType>(
recoilState: (scopedKey: ComponentStateKey) => RecoilState<StateType>,
recoilState: (scopedKey: RecoilComponentStateKey) => RecoilState<StateType>,
scopeId: string,
) => {
return useRecoilState<StateType>(

View File

@ -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<T extends ComponentStateKey> = Context<T | null>;
type ScopeInternalContext<T extends RecoilComponentStateKey> =
Context<T | null>;
export const createScopeInternalContext = <T extends ComponentStateKey>(
export const createScopeInternalContext = <T extends RecoilComponentStateKey>(
initialValue?: T,
) => {
return createContext<T | null>(

View File

@ -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<StateType> = (
scopedKey: ComponentStateKey,
scopedKey: RecoilComponentStateKey,
) => RecoilValueReadOnly<StateType>;

View File

@ -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<StateType> = (
scopedKey: ComponentStateKey,
scopedKey: RecoilComponentStateKey,
) => RecoilState<StateType>;

View File

@ -1,2 +1,2 @@
export const getScopeIdFromComponentId = (componentId: string) =>
`${componentId}-scope`;
`${componentId}`;

View File

@ -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<T>,
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.',
);
}
};

View File

@ -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<T>,
) => {
const context = useContext(Context);
return context;
};

View File

@ -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 = <Value>(
componentState: RecoilComponentState<Value>,
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,
});
};

View File

@ -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<ValueType>(
componentState: ComponentStateV2<ValueType>,
instanceIdFromProps?: string,
): RecoilState<ValueType>;
export function useRecoilComponentCallbackStateV2<ValueType>(
componentSelector: ComponentSelectorV2<ValueType>,
instanceIdFromProps?: string,
): RecoilState<ValueType>;
export function useRecoilComponentCallbackStateV2<ValueType>(
componentReadOnlySelector: ComponentReadOnlySelectorV2<ValueType>,
instanceIdFromProps?: string,
): RecoilValueReadOnly<ValueType>;
export function useRecoilComponentCallbackStateV2<
ValueType,
FamilyKey extends SerializableParam,
>(
componentFamilyState: ComponentFamilyStateV2<ValueType, FamilyKey>,
instanceIdFromProps?: string,
): (familyKey: FamilyKey) => RecoilState<ValueType>;
export function useRecoilComponentCallbackStateV2<
ValueType,
FamilyKey extends SerializableParam,
>(
componentFamilySelector: ComponentFamilySelectorV2<ValueType, FamilyKey>,
instanceIdFromProps?: string,
): (familyKey: FamilyKey) => RecoilState<ValueType>;
export function useRecoilComponentCallbackStateV2<
ValueType,
FamilyKey extends SerializableParam,
>(
componentFamilyReadOnlySelector: ComponentFamilyReadOnlySelectorV2<
ValueType,
FamilyKey
>,
instanceIdFromProps?: string,
): (familyKey: FamilyKey) => RecoilValueReadOnly<ValueType>;
export function useRecoilComponentCallbackStateV2<
ValueType,
FamilyKey extends SerializableParam,
>(
componentFamilyState: ComponentFamilyStateV2<ValueType, FamilyKey>,
instanceIdFromProps?: string,
): (familyKey: FamilyKey) => RecoilState<ValueType>;
export function useRecoilComponentCallbackStateV2<
ComponentState extends
| ComponentStateV2<ValueType>
| ComponentSelectorV2<ValueType>
| ComponentReadOnlySelectorV2<ValueType>
| ComponentFamilyStateV2<ValueType, FamilyKey>
| ComponentFamilySelectorV2<ValueType, FamilyKey>
| ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>,
ValueType,
FamilyKey extends SerializableParam = never,
>(
componentState: ComponentState,
instanceIdFromProps?: string,
):
| RecoilState<ValueType>
| RecoilValueReadOnly<ValueType>
| ((familyKey: FamilyKey) => RecoilState<ValueType>)
| ((familyKey: FamilyKey) => RecoilValueReadOnly<ValueType>) {
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,
});
}
}
}

View File

@ -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<StateType, FamilyKey>
| ComponentFamilySelectorV2<StateType, FamilyKey>
| ComponentFamilyReadOnlySelectorV2<StateType, FamilyKey>,
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 }),
);
}
}
};

View File

@ -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 = <StateType>(
componentState: RecoilComponentState<StateType>,
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 }),
);
};

View File

@ -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 = <StateType>(
componentState: ComponentStateV2<StateType>,
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 }));
};

View File

@ -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 = <StateType>(
componentState: ComponentState<StateType>,
componentState: RecoilComponentState<StateType>,
componentId?: string,
) => {
const componentContext = (window as any).componentContextStateMap?.get(

View File

@ -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 = <StateType>(
componentStateV2: ComponentStateV2<StateType>,
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 }));
};

View File

@ -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<any> | ComponentFamilyState<any, any>,
componentState: RecoilComponentState<any> | ComponentFamilyState<any, any>,
) => {
const componentContext = (window as any).componentContextStateMap?.get(
componentState.key,

View File

@ -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<StateType, FamilyKey>
| ComponentFamilySelectorV2<StateType, FamilyKey>,
familyKey: FamilyKey,
instanceIdFromProps?: string,
): SetterOrUpdater<StateType> => {
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,
}),
);
}
}
};

View File

@ -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 = <StateType>(
componentState: ComponentState<StateType>,
componentState: RecoilComponentState<StateType>,
componentId?: string,
) => {
const componentContext = (window as any).componentContextStateMap?.get(

View File

@ -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 = <ValueType>(
componentState: ComponentStateV2<ValueType>,
instanceIdFromProps?: string,
): SetterOrUpdater<ValueType> => {
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 }));
};

View File

@ -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<ComponentStateTypeV2, 'ComponentFamilyReadOnlySelector'>;
key: string;
selectorFamily: (
componentFamilyStateKey: ComponentFamilyStateKeyV2<FamilyKey>,
) => RecoilValueReadOnly<StateType>;
};

View File

@ -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<ComponentStateTypeV2, 'ComponentFamilySelector'>;
key: string;
selectorFamily: (
componentFamilyStateKey: ComponentFamilyStateKeyV2<FamilyKey>,
) => RecoilState<StateType>;
};

View File

@ -0,0 +1,7 @@
import { ComponentStateKeyV2 } from '@/ui/utilities/state/component-state/types/ComponentStateKeyV2';
import { SerializableParam } from 'recoil';
export type ComponentFamilyStateKeyV2<FamilyKey extends SerializableParam> =
ComponentStateKeyV2 & {
familyKey: FamilyKey;
};

View File

@ -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<ComponentStateTypeV2, 'ComponentFamilyState'>;
key: string;
atomFamily: (
componentFamilyStateKey: ComponentFamilyStateKeyV2<FamilyKey>,
) => RecoilState<StateType>;
};

View File

@ -0,0 +1,4 @@
import { Context } from 'react';
export type ComponentInstanceStateContext<T extends { instanceId: string }> =
Context<T | null>;

View File

@ -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<StateType> = {
type: Extract<ComponentStateTypeV2, 'ComponentReadOnlySelector'>;
key: string;
selectorFamily: (
componentStateKey: ComponentStateKeyV2,
) => RecoilValueReadOnly<StateType>;
};

View File

@ -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<StateType> = {
type: Extract<ComponentStateTypeV2, 'ComponentSelector'>;
key: string;
selectorFamily: (
componentStateKey: ComponentStateKeyV2,
) => RecoilState<StateType>;
};

View File

@ -1,8 +0,0 @@
import { RecoilState } from 'recoil';
import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey';
export type ComponentState<StateType> = {
key: string;
atomFamily: (componentStateKey: ComponentStateKey) => RecoilState<StateType>;
};

View File

@ -1,3 +0,0 @@
export type ComponentStateKey = {
scopeId: string;
};

View File

@ -0,0 +1,3 @@
export type ComponentStateKeyV2 = {
instanceId: string;
};

View File

@ -0,0 +1,7 @@
export type ComponentStateTypeV2 =
| 'ComponentState'
| 'ComponentFamilyState'
| 'ComponentSelector'
| 'ComponentReadOnlySelector'
| 'ComponentFamilySelector'
| 'ComponentFamilyReadOnlySelector';

View File

@ -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<StateType> = {
type: Extract<ComponentStateTypeV2, 'ComponentState'>;
key: string;
atomFamily: (
componentStateKey: ComponentStateKeyV2,
) => RecoilState<StateType>;
};

View File

@ -0,0 +1,9 @@
import { RecoilState } from 'recoil';
import { RecoilComponentStateKey } from './RecoilComponentStateKey';
export type RecoilComponentState<StateType> = {
key: string;
atomFamily: (
componentStateKey: RecoilComponentStateKey,
) => RecoilState<StateType>;
};

View File

@ -0,0 +1,3 @@
export type RecoilComponentStateKey = {
scopeId: string;
};

View File

@ -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<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>;
set?: SelectorSetter<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>;
componentInstanceContext: ComponentInstanceStateContext<any> | null;
}):
| ComponentFamilySelectorV2<ValueType, FamilyKey>
| ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey> => {
if (isDefined(componentInstanceContext)) {
globalComponentInstanceContextMap.set(key, componentInstanceContext);
}
if (isDefined(set)) {
return {
type: 'ComponentFamilySelector',
key,
selectorFamily: selectorFamily<
ValueType,
ComponentFamilyStateKeyV2<FamilyKey>
>({
key,
get,
set,
}),
} satisfies ComponentFamilySelectorV2<ValueType, FamilyKey>;
} else {
return {
type: 'ComponentFamilyReadOnlySelector',
key,
selectorFamily: selectorFamily<
ValueType,
ComponentFamilyStateKeyV2<FamilyKey>
>({
key,
get,
}),
} satisfies ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>;
}
};

View File

@ -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<ValueType> = {
type CreateComponentFamilyStateArgs<ValueType> = {
key: string;
defaultValue: ValueType;
componentContext: ScopeInternalContext<any> | null;
componentInstanceContext: ComponentInstanceStateContext<any> | null;
effects?: AtomEffect<ValueType>[];
};
@ -18,22 +20,22 @@ export const createComponentFamilyStateV2 = <
key,
effects,
defaultValue,
componentContext,
}: CreateComponentFamilyStateV2Type<ValueType>) => {
if (isDefined(componentContext)) {
if (!isDefined((window as any).componentContextStateMap)) {
(window as any).componentContextStateMap = new Map();
}
(window as any).componentContextStateMap.set(key, componentContext);
componentInstanceContext,
}: CreateComponentFamilyStateArgs<ValueType>): ComponentFamilyStateV2<
ValueType,
FamilyKey
> => {
if (isDefined(componentInstanceContext)) {
globalComponentInstanceContextMap.set(key, componentInstanceContext);
}
return {
type: 'ComponentFamilyState',
key,
atomFamily: atomFamily<ValueType, ComponentFamilyStateKey<FamilyKey>>({
atomFamily: atomFamily<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>({
key,
default: defaultValue,
effects,
}),
};
} satisfies ComponentFamilyStateV2<ValueType, FamilyKey>;
};

View File

@ -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<T | null>(
initialValue ?? null,
) as ComponentInstanceStateContext<T>;
};

View File

@ -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 = <ValueType>({
@ -8,9 +8,9 @@ export const createComponentReadOnlySelector = <ValueType>({
get,
}: {
key: string;
get: SelectorGetter<ValueType, ComponentStateKey>;
get: SelectorGetter<ValueType, RecoilComponentStateKey>;
}) => {
return selectorFamily<ValueType, ComponentStateKey>({
return selectorFamily<ValueType, RecoilComponentStateKey>({
key,
get,
});

View File

@ -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 = <ValueType>({
set,
}: {
key: string;
get: SelectorGetter<ValueType, ComponentStateKey>;
set: SelectorSetter<ValueType, ComponentStateKey>;
get: SelectorGetter<ValueType, RecoilComponentStateKey>;
set: SelectorSetter<ValueType, RecoilComponentStateKey>;
}) => {
return selectorFamily<ValueType, ComponentStateKey>({
return selectorFamily<ValueType, RecoilComponentStateKey>({
key,
get,
set,

View File

@ -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 = <ValueType>({
key,
get,
set,
instanceContext,
}: {
key: string;
get: SelectorGetter<ValueType, ComponentStateKeyV2>;
set?: SelectorSetter<ValueType, ComponentStateKeyV2>;
instanceContext: ComponentInstanceStateContext<any> | null;
}): ComponentSelectorV2<ValueType> | ComponentReadOnlySelectorV2<ValueType> => {
if (isDefined(instanceContext)) {
globalComponentInstanceContextMap.set(key, instanceContext);
}
if (isDefined(set)) {
return {
type: 'ComponentSelector',
key,
selectorFamily: selectorFamily<ValueType, ComponentStateKeyV2>({
key,
get,
set,
}),
} satisfies ComponentSelectorV2<ValueType>;
} else {
return {
type: 'ComponentReadOnlySelector',
key,
selectorFamily: selectorFamily<ValueType, ComponentStateKeyV2>({
key,
get,
}),
} satisfies ComponentReadOnlySelectorV2<ValueType>;
}
};

View File

@ -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<ValueType> = {
key: string;
@ -13,7 +13,7 @@ export const createComponentState = <ValueType>({
defaultValue,
effects,
}: CreateComponentStateType<ValueType>) => {
return atomFamily<ValueType, ComponentStateKey>({
return atomFamily<ValueType, RecoilComponentStateKey>({
key,
default: defaultValue,
effects: effects,

View File

@ -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<ValueType> = {
type CreateComponentInstanceStateArgs<ValueType> = {
key: string;
defaultValue: ValueType;
componentContext?: ScopeInternalContext<any> | null;
componentInstanceContext: ComponentInstanceStateContext<any> | null;
effects?: AtomEffect<ValueType>[];
};
export const createComponentStateV2 = <ValueType>({
key,
defaultValue,
componentContext,
componentInstanceContext,
effects,
}: CreateComponentStateV2Type<ValueType>): ComponentState<ValueType> => {
if (isDefined(componentContext)) {
if (!isDefined((window as any).componentContextStateMap)) {
(window as any).componentContextStateMap = new Map();
}
(window as any).componentContextStateMap.set(key, componentContext);
}: CreateComponentInstanceStateArgs<ValueType>): ComponentStateV2<ValueType> => {
if (isDefined(componentInstanceContext)) {
globalComponentInstanceContextMap.set(key, componentInstanceContext);
}
return {
type: 'ComponentState',
key,
atomFamily: atomFamily<ValueType, ComponentStateKey>({
atomFamily: atomFamily<ValueType, ComponentStateKeyV2>({
key,
default: defaultValue,
effects: effects,
}),
};
} satisfies ComponentStateV2<ValueType>;
};

View File

@ -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<ValueType> = {
key: string;
defaultValue: ValueType;
componentContext?: ScopeInternalContext<any> | null;
effects?: AtomEffect<ValueType>[];
};
export const createComponentStateV2_alpha = <ValueType>({
key,
defaultValue,
componentContext,
effects,
}: CreateComponentStateV2Type<ValueType>): RecoilComponentState<ValueType> => {
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<ValueType, RecoilComponentStateKey>({
key,
default: defaultValue,
effects: effects,
}),
};
};

View File

@ -0,0 +1,9 @@
import { createContext } from 'react';
export const createEventContext = <
Events extends Record<string, (...args: any) => void>,
>(
initialValue?: Events,
) => {
return createContext<Events>((initialValue ?? {}) as Events);
};

View File

@ -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 = <StateType>(
componentSelector: (
componentStateKey: ComponentStateKey,
componentStateKey: RecoilComponentStateKey,
) => RecoilValueReadOnly<StateType>,
scopeId: string,
) => {

View File

@ -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 = <StateType>(
componentSelector: (
componentStateKey: ComponentStateKey,
componentStateKey: RecoilComponentStateKey,
) => RecoilState<StateType>,
scopeId: string,
) => {

View File

@ -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 = <StateType>(
componentState: (
componentStateKey: ComponentStateKey,
componentStateKey: RecoilComponentStateKey,
) => RecoilState<StateType>,
scopeId: string,
) => {

View File

@ -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<any> {
return (window as any).componentComponentStateContextMap.get(key);
}
public set(key: string, context: ComponentInstanceStateContext<any>) {
(window as any).componentComponentStateContextMap.set(key, context);
}
}
export const globalComponentInstanceContextMap =
new ComponentInstanceContextMap();

View File

@ -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 (
<Dropdown

View File

@ -2,18 +2,20 @@ import { IconArrowDown, IconArrowUp } from 'twenty-ui';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts';
import { useDeleteCombinedViewSorts } from '@/views/hooks/useDeleteCombinedViewSorts';
import { useUpsertCombinedViewSorts } from '@/views/hooks/useUpsertCombinedViewSorts';
type EditableSortChipProps = {
viewSort: Sort;
};
export const EditableSortChip = ({ viewSort }: EditableSortChipProps) => {
const { removeCombinedViewSort, upsertCombinedViewSort } =
useCombinedViewSorts();
const { deleteCombinedViewSort } = useDeleteCombinedViewSorts();
const { upsertCombinedViewSort } = useUpsertCombinedViewSorts();
const handleRemoveClick = () => {
removeCombinedViewSort(viewSort.fieldMetadataId);
deleteCombinedViewSort(viewSort.fieldMetadataId);
};
const handleClick = () => {

View File

@ -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 <></>;

View File

@ -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');

View File

@ -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 = ({
<StyledContainer>
{currentViewWithCombinedFiltersAndSorts?.key !== 'INDEX' ? (
<ButtonGroup size="small" accent="blue">
<Button title="Update view" onClick={handleViewUpdate} />
<Button title="Update view" onClick={handleUpdateViewClick} />
<Dropdown
dropdownId={UPDATE_VIEW_BUTTON_DROPDOWN_ID}
dropdownHotkeyScope={hotkeyScope}
@ -103,7 +110,7 @@ export const UpdateViewButtonGroup = ({
<>
<DropdownMenuItemsContainer>
<MenuItem
onClick={handleViewCreate}
onClick={handleCreateViewClick}
LeftIcon={IconPlus}
text="Create view"
/>
@ -115,7 +122,7 @@ export const UpdateViewButtonGroup = ({
) : (
<Button
title="Save as new view"
onClick={handleViewCreate}
onClick={handleSaveAsNewViewClick}
accent="blue"
size="small"
variant="secondary"

View File

@ -4,7 +4,7 @@ import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObje
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters';
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
import { useParams } from 'react-router-dom';
type VariantFilterChipProps = {
@ -16,7 +16,7 @@ export const VariantFilterChip = ({
viewFilter,
viewBarId,
}: VariantFilterChipProps) => {
const { removeCombinedViewFilter } = useCombinedViewFilters();
const { deleteCombinedViewFilter } = useDeleteCombinedViewFilters();
const { objectNamePlural } = useParams();
@ -32,7 +32,7 @@ export const VariantFilterChip = ({
const { getIcon } = useIcons();
const handleRemoveClick = () => {
removeCombinedViewFilter(viewFilter.id);
deleteCombinedViewFilter(viewFilter.id);
if (
viewFilter.definition.label === 'Deleted' &&
viewFilter.operand === 'isNotEmpty'

View File

@ -13,12 +13,12 @@ import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
import { ViewBarPageTitle } from '@/views/components/ViewBarPageTitle';
import { ViewBarSkeletonLoader } from '@/views/components/ViewBarSkeletonLoader';
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
import { ViewScope } from '@/views/scopes/ViewScope';
import { GraphQLView } from '@/views/types/GraphQLView';
import { ViewPickerDropdown } from '@/views/view-picker/components/ViewPickerDropdown';
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
import { ViewEventContext } from '@/views/events/contexts/ViewEventContext';
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
import { ViewBarDetails } from './ViewBarDetails';
@ -41,15 +41,13 @@ export const ViewBar = ({
const sortDropdownId = 'view-sort';
const loading = useIsPrefetchLoading();
if (!objectNamePlural) {
return;
}
return (
<ViewScope
viewScopeId={viewBarId}
onCurrentViewChange={onCurrentViewChange}
>
<ViewEventContext.Provider value={{ onCurrentViewChange }}>
<ViewBarEffect viewBarId={viewBarId} />
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
<ViewBarSortEffect sortDropdownId={sortDropdownId} />
@ -95,6 +93,6 @@ export const ViewBar = ({
/>
}
/>
</ViewScope>
</ViewEventContext.Provider>
);
};

View File

@ -1,20 +1,25 @@
import styled from '@emotion/styled';
import { ReactNode, useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { AddObjectFilterFromDetailsButton } from '@/object-record/object-filter-dropdown/components/AddObjectFilterFromDetailsButton';
import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { EditableFilterDropdownButton } from '@/views/components/EditableFilterDropdownButton';
import { EditableSortChip } from '@/views/components/EditableSortChip';
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
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 { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState';
import { canPersistViewComponentFamilySelector } from '@/views/states/selectors/canPersistViewComponentFamilySelector';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import { isDefined } from 'twenty-ui';
import { VariantFilterChip } from './VariantFilterChip';
export type ViewBarDetailsProps = {
@ -99,26 +104,30 @@ export const ViewBarDetails = ({
filterDropdownId,
viewBarId,
}: ViewBarDetailsProps) => {
const {
canPersistViewSelector,
isViewBarExpandedState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
} = useViewStates();
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
const isViewBarExpanded = useRecoilValue(isViewBarExpandedState);
const { hasFiltersQueryParams } = useViewFromQueryParams();
const canPersistView = useRecoilValue(canPersistViewSelector());
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
);
const availableSortDefinitions = useRecoilValue(
availableSortDefinitionsState,
const viewId = currentViewWithCombinedFiltersAndSorts?.id;
const isViewBarExpanded = useRecoilComponentValueV2(
isViewBarExpandedComponentState,
);
const { resetCurrentView } = useResetCurrentView();
const { hasFiltersQueryParams } = useViewFromQueryParams();
const canPersistView = useRecoilComponentFamilyValueV2(
canPersistViewComponentFamilySelector,
{ viewId },
);
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
);
const availableSortDefinitions = useRecoilComponentValueV2(
availableSortDefinitionsComponentState,
);
const { resetUnsavedViewStates } = useResetUnsavedViewStates();
const canResetView = canPersistView && !hasFiltersQueryParams;
const { otherViewFilters, defaultViewFilters } = useMemo(() => {
@ -145,7 +154,9 @@ export const ViewBarDetails = ({
}, [currentViewWithCombinedFiltersAndSorts]);
const handleCancelClick = () => {
resetCurrentView();
if (isDefined(viewId)) {
resetUnsavedViewStates(viewId);
}
};
const shouldExpandViewBar =

View File

@ -1,9 +1,11 @@
import { isUndefined } from '@sniptt/guards';
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useContext, useEffect, useState } from 'react';
import { useViewStates } from '@/views/hooks/internal/useViewStates';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewEventContext } from '@/views/events/contexts/ViewEventContext';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState';
import { View } from '@/views/types/View';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
@ -14,21 +16,22 @@ type ViewBarEffectProps = {
export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
const { currentViewWithCombinedFiltersAndSorts } =
useGetCurrentView(viewBarId);
const {
onCurrentViewChangeState,
availableFilterDefinitionsState,
isPersistingViewFieldsState,
} = useViewStates(viewBarId);
const { onCurrentViewChange } = useContext(ViewEventContext);
const [currentViewSnapshot, setCurrentViewSnapshot] = useState<
View | undefined
>(undefined);
const onCurrentViewChange = useRecoilValue(onCurrentViewChangeState);
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
viewBarId,
);
const isPersistingViewFields = useRecoilComponentValueV2(
isPersistingViewFieldsComponentState,
viewBarId,
);
const isPersistingViewFields = useRecoilValue(isPersistingViewFieldsState);
useEffect(() => {
if (

View File

@ -1,12 +1,15 @@
import { useEffect } from 'react';
import { isNonEmptyString } from '@sniptt/guards';
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { useViewStates } from '@/views/hooks/internal/useViewStates';
import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { isDefined } from '~/utils/isDefined';
type ViewBarFilterEffectProps = {
@ -16,17 +19,15 @@ type ViewBarFilterEffectProps = {
export const ViewBarFilterEffect = ({
filterDropdownId,
}: ViewBarFilterEffectProps) => {
const { availableFilterDefinitionsState } = useViewStates();
const { upsertCombinedViewFilter } = useCombinedViewFilters();
const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters();
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
const availableFilterDefinitions = useRecoilComponentValueV2(
availableFilterDefinitionsComponentState,
);
const {
setAvailableFilterDefinitions,
setOnFilterSelect,
filterDefinitionUsedInDropdownState,
setObjectFilterDropdownSelectedRecordIds,
@ -37,6 +38,12 @@ export const ViewBarFilterEffect = ({
filterDefinitionUsedInDropdownState,
);
// TODO: verify this instance id works
const setAvailableFilterDefinitions = useSetRecoilComponentStateV2(
availableFilterDefinitionsComponentState,
filterDropdownId,
);
useEffect(() => {
if (isDefined(availableFilterDefinitions)) {
setAvailableFilterDefinitions(availableFilterDefinitions);

View File

@ -1,10 +1,12 @@
import { useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useSetRecoilState } from 'recoil';
import { useSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useSortDropdown';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { useViewStates } from '@/views/hooks/internal/useViewStates';
import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useUpsertCombinedViewSorts } from '@/views/hooks/useUpsertCombinedViewSorts';
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
import { isDefined } from '~/utils/isDefined';
type ViewBarSortEffectProps = {
@ -14,23 +16,23 @@ type ViewBarSortEffectProps = {
export const ViewBarSortEffect = ({
sortDropdownId,
}: ViewBarSortEffectProps) => {
const { availableSortDefinitionsState } = useViewStates();
const { upsertCombinedViewSort } = useCombinedViewSorts();
const { upsertCombinedViewSort } = useUpsertCombinedViewSorts();
const availableSortDefinitions = useRecoilValue(
availableSortDefinitionsState,
// TDOO: verify this instance id works
const availableSortDefinitions = useRecoilComponentValueV2(
availableSortDefinitionsComponentState,
);
const {
availableSortDefinitionsState: availableSortDefinitionsInSortDropdownState,
onSortSelectState,
} = useSortDropdown({
const { onSortSelectState } = useSortDropdown({
sortDropdownId,
});
const setAvailableSortDefinitionsInSortDropdown = useSetRecoilState(
availableSortDefinitionsInSortDropdownState,
);
// TDOO: verify this instance id works
const setAvailableSortDefinitionsInSortDropdown =
useSetRecoilComponentStateV2(
availableSortDefinitionsComponentState,
sortDropdownId,
);
const setOnSortSelect = useSetRecoilState(onSortSelectState);
useEffect(() => {

View File

@ -0,0 +1,8 @@
import { View } from '@/views/types/View';
import { createEventContext } from '~/utils/createEventContext';
type ViewEventContextType = {
onCurrentViewChange: (view: View | undefined) => void | Promise<void>;
};
export const ViewEventContext = createEventContext<ViewEventContextType>();

View File

@ -1,91 +0,0 @@
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { extractComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { availableFieldDefinitionsComponentState } from '@/views/states/availableFieldDefinitionsComponentState';
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState';
import { isCurrentViewKeyIndexComponentState } from '@/views/states/isCurrentViewIndexComponentState';
import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState';
import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState';
import { onCurrentViewChangeComponentState } from '@/views/states/onCurrentViewChangeComponentState';
import { canPersistViewComponentSelector } from '@/views/states/selectors/canPersistViewComponentSelector';
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
export const useViewStates = (viewComponentId?: string) => {
const componentId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
viewComponentId,
);
return {
componentId,
currentViewIdState: extractComponentState(
currentViewIdComponentState,
componentId,
),
availableFieldDefinitionsState: extractComponentState(
availableFieldDefinitionsComponentState,
componentId,
),
availableFilterDefinitionsState: extractComponentState(
availableFilterDefinitionsComponentState,
componentId,
),
availableSortDefinitionsState: extractComponentState(
availableSortDefinitionsComponentState,
componentId,
),
canPersistViewSelector: extractComponentReadOnlySelector(
canPersistViewComponentSelector,
componentId,
),
isViewBarExpandedState: extractComponentState(
isViewBarExpandedComponentState,
componentId,
),
onCurrentViewChangeState: extractComponentState(
onCurrentViewChangeComponentState,
componentId,
),
entityCountInCurrentViewState: extractComponentState(
entityCountInCurrentViewComponentState,
componentId,
),
viewObjectMetadataIdState: extractComponentState(
viewObjectMetadataIdComponentState,
componentId,
),
unsavedToUpsertViewFiltersState: extractComponentState(
unsavedToUpsertViewFiltersComponentState,
componentId,
),
unsavedToUpsertViewSortsState: extractComponentState(
unsavedToUpsertViewSortsComponentState,
componentId,
),
unsavedToDeleteViewFilterIdsState: extractComponentState(
unsavedToDeleteViewFilterIdsComponentState,
componentId,
),
unsavedToDeleteViewSortIdsState: extractComponentState(
unsavedToDeleteViewSortIdsComponentState,
componentId,
),
isPersistingViewFieldsState: extractComponentState(
isPersistingViewFieldsComponentState,
componentId,
),
isCurrentViewKeyIndexState: extractComponentState(
isCurrentViewKeyIndexComponentState,
componentId,
),
};
};

Some files were not shown because too many files have changed in this diff Show More