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:
@ -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
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
@ -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 <></>;
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
Reference in New Issue
Block a user