Feat/improve new views (#2298)

* POC new recoil injected scoped states

* Finished useViewScopedState refactor

* Finished refactor

* Renamed mappers

* Fixed update view fields bug

* Post merge

* Complete refactor

* Fix tests

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-11-04 09:28:55 +01:00
committed by GitHub
parent e70ef58f97
commit 53072298bc
42 changed files with 1018 additions and 885 deletions

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries';
@ -11,10 +11,10 @@ import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFi
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { ViewType } from '@/views/types/ViewType';
import { viewFieldsToBoardFieldDefinitions } from '@/views/utils/viewFieldsToBoardFieldDefinitions';
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
import {
Pipeline,
PipelineProgressableType,
@ -37,7 +37,11 @@ export const HooksCompanyBoardEffect = () => {
setViewType,
} = useView();
const { currentViewFilters, currentViewFields } = useViewGetStates();
const { currentViewFiltersState, currentViewFieldsState } =
useViewScopedStates();
const currentViewFields = useRecoilValue(currentViewFieldsState);
const currentViewFilters = useRecoilValue(currentViewFiltersState);
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
@ -159,7 +163,7 @@ export const HooksCompanyBoardEffect = () => {
useEffect(() => {
if (currentViewFields) {
setBoardCardFields(
viewFieldsToBoardFieldDefinitions(
mapViewFieldsToBoardFieldDefinitions(
currentViewFields,
pipelineAvailableFieldDefinitions,
),

View File

@ -16,12 +16,11 @@ import { tableFiltersScopedState } from '@/ui/object/record-table/states/tableFi
import { tableSortsScopedState } from '@/ui/object/record-table/states/tableSortsScopedState';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField';
import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions';
import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters';
import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import {
UpdateOneCompanyMutationVariables,
useGetCompaniesQuery,
@ -58,9 +57,6 @@ export const CompanyTable = () => {
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
const { persistViewFields } = useViewFields(viewScopeId);
const { setCurrentViewFields } = useView({
viewScopeId,
});
const { setContextMenuEntries, setActionBarEntries } =
useCompanyTableContextMenuEntries();
@ -102,25 +98,24 @@ export const CompanyTable = () => {
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
viewFieldsToColumnDefinitions(
mapViewFieldsToColumnDefinitions(
viewFields,
companiesAvailableFieldDefinitions,
),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(viewFiltersToFilters(viewFilters));
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(viewSortsToSorts(viewSorts));
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<TableContext.Provider
value={{
onColumnsChange: useRecoilCallback(() => (columns) => {
setCurrentViewFields?.(columnDefinitionsToViewFields(columns));
persistViewFields(columnDefinitionsToViewFields(columns));
persistViewFields(mapColumnDefinitionsToViewFields(columns));
}),
}}
>

View File

@ -10,12 +10,11 @@ import { tableFiltersScopedState } from '@/ui/object/record-table/states/tableFi
import { tableSortsScopedState } from '@/ui/object/record-table/states/tableSortsScopedState';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField';
import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions';
import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters';
import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import { useObjectMetadataItemInContext } from '../hooks/useObjectMetadataItemInContext';
import { useUpdateOneObject } from '../hooks/useUpdateOneObject';
@ -46,9 +45,6 @@ export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => {
const viewScopeId = objectNamePlural ?? '';
const { persistViewFields } = useViewFields(viewScopeId);
const { setCurrentViewFields } = useView({
viewScopeId,
});
const setTableColumns = useSetRecoilState(
tableColumnsScopedState(tableScopeId),
@ -81,22 +77,21 @@ export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => {
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
viewFieldsToColumnDefinitions(viewFields, columnDefinitions),
mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(viewFiltersToFilters(viewFilters));
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(viewSortsToSorts(viewSorts));
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<TableContext.Provider
value={{
onColumnsChange: useRecoilCallback(() => (columns) => {
setCurrentViewFields?.(columnDefinitionsToViewFields(columns));
persistViewFields(columnDefinitionsToViewFields(columns));
persistViewFields(mapColumnDefinitionsToViewFields(columns));
}),
}}
>

View File

@ -1,10 +1,11 @@
import styled from '@emotion/styled';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useSetRecoilState } from 'recoil';
import { peopleAvailableFieldDefinitions } from '@/people/constants/peopleAvailableFieldDefinitions';
import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition';
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePersonTableContextMenuEntries';
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { RecordTableEffect } from '@/ui/object/record-table/components/RecordTableEffect';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
@ -14,14 +15,14 @@ import { TableOptionsDropdown } from '@/ui/object/record-table/options/component
import { tableColumnsScopedState } from '@/ui/object/record-table/states/tableColumnsScopedState';
import { tableFiltersScopedState } from '@/ui/object/record-table/states/tableFiltersScopedState';
import { tableSortsScopedState } from '@/ui/object/record-table/states/tableSortsScopedState';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField';
import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions';
import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters';
import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import {
UpdateOnePersonMutationVariables,
useGetPeopleQuery,
@ -49,9 +50,6 @@ export const PersonTable = () => {
const upsertRecordTableItem = useUpsertRecordTableItem();
const { persistViewFields } = useViewFields(viewScopeId);
const { setCurrentViewFields } = useView({
viewScopeId,
});
const { setContextMenuEntries, setActionBarEntries } =
usePersonTableContextMenuEntries();
@ -68,6 +66,10 @@ export const PersonTable = () => {
});
};
const handleColumnChange = (columns: ColumnDefinition<FieldMetadata>[]) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
};
const { openPersonSpreadsheetImport: onImport } =
useSpreadsheetPersonImport();
@ -82,26 +84,23 @@ export const PersonTable = () => {
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
viewFieldsToColumnDefinitions(
mapViewFieldsToColumnDefinitions(
viewFields,
peopleAvailableFieldDefinitions,
),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(viewFiltersToFilters(viewFilters));
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(viewSortsToSorts(viewSorts));
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<TableContext.Provider
value={{
onColumnsChange: useRecoilCallback(() => (columns) => {
setCurrentViewFields?.(columnDefinitionsToViewFields(columns));
persistViewFields(columnDefinitionsToViewFields(columns));
}),
onColumnsChange: handleColumnChange,
}}
>
<ViewBar

View File

@ -1,5 +1,5 @@
import { useContext, useRef, useState } from 'react';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { v4 } from 'uuid';
@ -24,8 +24,8 @@ import { ThemeColor } from '@/ui/theme/constants/colors';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { useBoardCardFields } from '../hooks/useBoardCardFields';
import { boardColumnsState } from '../states/boardColumnsState';
@ -52,9 +52,12 @@ export const BoardOptionsDropdownContent = ({
onStageAdd,
}: BoardOptionsDropdownContentProps) => {
const { setViewEditMode, handleViewNameSubmit } = useView();
const { viewEditMode, currentView } = useViewGetStates();
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
const { BoardRecoilScopeContext } = useContext(BoardContext);
const viewEditMode = useRecoilValue(viewEditModeState);
const currentView = useRecoilValue(currentViewSelector);
const stageInputRef = useRef<HTMLInputElement>(null);
const viewEditInputRef = useRef<HTMLInputElement>(null);

View File

@ -10,6 +10,7 @@ import { isFieldEmail } from '../../types/guards/isFieldEmail';
import { isFieldMoney } from '../../types/guards/isFieldMoney';
import { isFieldNumber } from '../../types/guards/isFieldNumber';
import { isFieldPhone } from '../../types/guards/isFieldPhone';
import { isFieldProbability } from '../../types/guards/isFieldProbability';
import { isFieldRelation } from '../../types/guards/isFieldRelation';
import { isFieldRelationValue } from '../../types/guards/isFieldRelationValue';
import { isFieldText } from '../../types/guards/isFieldText';
@ -34,6 +35,7 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
isFieldURL(fieldDefinition) ||
isFieldDate(fieldDefinition) ||
isFieldNumber(fieldDefinition) ||
isFieldProbability(fieldDefinition) ||
isFieldMoney(fieldDefinition) ||
isFieldEmail(fieldDefinition) ||
isFieldBoolean(fieldDefinition) ||

View File

@ -1,5 +1,6 @@
import { useCallback, useRef, useState } from 'react';
import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/display/icon';
@ -12,8 +13,8 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useView } from '@/views/hooks/useView';
import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { useTableColumns } from '../../hooks/useTableColumns';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
@ -29,7 +30,10 @@ export const TableOptionsDropdownContent = ({
onImport?: () => void;
}) => {
const { setViewEditMode, handleViewNameSubmit } = useView();
const { viewEditMode, currentView } = useViewGetStates();
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
const viewEditMode = useRecoilValue(viewEditModeState);
const currentView = useRecoilValue(currentViewSelector);
const { closeDropdown } = useDropdown();

View File

@ -0,0 +1,7 @@
import { RecoilValueReadOnly } from 'recoil';
import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
export type RecoilScopedSelector<StateType> = (
scopedKey: ScopedStateKey,
) => RecoilValueReadOnly<StateType>;

View File

@ -0,0 +1,7 @@
import { RecoilState } from 'recoil';
import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
export type RecoilScopedState<StateType> = (
scopedKey: ScopedStateKey,
) => RecoilState<StateType>;

View File

@ -0,0 +1,30 @@
import {
GetCallback,
GetRecoilValue,
Loadable,
RecoilValue,
selectorFamily,
WrappedValue,
} from 'recoil';
import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
type SelectorGetter<T, P> = (
param: P,
) => (opts: {
get: GetRecoilValue;
getCallback: GetCallback;
}) => Promise<T> | RecoilValue<T> | Loadable<T> | WrappedValue<T> | T;
export const createScopedSelector = <ValueType>({
key,
get,
}: {
key: string;
get: SelectorGetter<ValueType, ScopedStateKey>;
}) => {
return selectorFamily<ValueType, ScopedStateKey>({
key,
get,
});
};

View File

@ -0,0 +1,19 @@
import { RecoilState, SerializableParam } from 'recoil';
import { ScopedFamilyStateKey } from '../scopes-internal/types/ScopedFamilyStateKey';
export const getScopedFamilyState = <
StateType,
FamilyKey extends SerializableParam,
>(
recoilState: (
scopedFamilyKey: ScopedFamilyStateKey<FamilyKey>,
) => RecoilState<StateType>,
scopeId: string,
familyKey: FamilyKey,
) => {
return recoilState({
scopeId,
familyKey: familyKey || ('' as FamilyKey),
});
};

View File

@ -0,0 +1,10 @@
import { RecoilScopedSelector } from '../types/RecoilScopedSelector';
export const getScopedSelector = <StateType>(
recoilScopedState: RecoilScopedSelector<StateType>,
scopeId: string,
) => {
return recoilScopedState({
scopeId,
});
};

View File

@ -0,0 +1,10 @@
import { RecoilScopedState } from '../types/RecoilScopedState';
export const getScopedState = <StateType>(
recoilScopedState: RecoilScopedState<StateType>,
scopeId: string,
) => {
return recoilScopedState({
scopeId,
});
};

View File

@ -0,0 +1,15 @@
import { Snapshot } from 'recoil';
import { RecoilScopedSelector } from '../types/RecoilScopedSelector';
import { getScopedSelector } from './getScopedSelector';
export const getSnapshotScopedSelector = <StateType>(
snapshot: Snapshot,
scopedState: RecoilScopedSelector<StateType>,
scopeId: string,
) => {
return snapshot
.getLoadable(getScopedSelector(scopedState, scopeId))
.getValue();
};

View File

@ -0,0 +1,13 @@
import { Snapshot } from 'recoil';
import { RecoilScopedState } from '../types/RecoilScopedState';
import { getScopedState } from './getScopedState';
export const getSnapshotScopedValue = <StateType>(
snapshot: Snapshot,
scopedState: RecoilScopedState<StateType>,
scopeId: string,
) => {
return snapshot.getLoadable(getScopedState(scopedState, scopeId)).getValue();
};

View File

@ -0,0 +1,8 @@
import { RecoilState, RecoilValueReadOnly, Snapshot } from 'recoil';
export const getSnapshotValue = <StateType>(
snapshot: Snapshot,
state: RecoilState<StateType> | RecoilValueReadOnly<StateType>,
) => {
return snapshot.getLoadable(state).getValue();
};

View File

@ -1,5 +1,6 @@
import { useCallback, useState } from 'react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconChevronDown, IconPlus } from '@/ui/display/icon';
@ -10,13 +11,14 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useView } from '@/views/hooks/useView';
import { useViewGetStates } from '../hooks/useViewGetStates';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
const StyledContainer = styled.div`
display: inline-flex;
margin-right: ${({ theme }) => theme.spacing(2)};
position: relative;
`;
export type UpdateViewButtonGroupProps = {
hotkeyScope: string;
onViewEditModeChange?: () => void;
@ -28,7 +30,11 @@ export const UpdateViewButtonGroup = ({
}: UpdateViewButtonGroupProps) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { updateCurrentView, setViewEditMode } = useView();
const { canPersistFilters, canPersistSorts } = useViewGetStates();
const { canPersistFiltersSelector, canPersistSortsSelector } =
useViewScopedStates();
const canPersistFilters = useRecoilValue(canPersistFiltersSelector);
const canPersistSorts = useRecoilValue(canPersistSortsSelector);
const canPersistView = canPersistFilters || canPersistSorts;

View File

@ -1,4 +1,5 @@
import { ReactNode } from 'react';
import { useRecoilValue } from 'recoil';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { TopBar } from '@/ui/layout/top-bar/TopBar';
@ -8,8 +9,8 @@ import { FiltersHotkeyScope } from '@/ui/object/object-filter-dropdown/types/Fil
import { ObjectSortDropdownButton } from '@/ui/object/object-sort-dropdown/components/ObjectSortDropdownButton';
import { ObjectSortDropdownScope } from '@/ui/object/object-sort-dropdown/scopes/ObjectSortDropdownScope';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { useViewGetStates } from '../hooks/useViewGetStates';
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
@ -32,8 +33,16 @@ export const ViewBar = ({
dropdownScopeId: optionsDropdownScopeId,
});
const { upsertViewSort, upsertViewFilter } = useView();
const { availableFilterDefinitions, availableSortDefinitions } =
useViewGetStates();
const { availableFilterDefinitionsState, availableSortDefinitionsState } =
useViewScopedStates();
const availableFilterDefinitions = useRecoilValue(
availableFilterDefinitionsState,
);
const availableSortDefinitions = useRecoilValue(
availableSortDefinitionsState,
);
return (
<ObjectFilterDropdownScope

View File

@ -1,12 +1,13 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index';
import { AddObjectFilterFromDetailsButton } from '@/ui/object/object-filter-dropdown/components/AddObjectFilterFromDetailsButton';
import { getOperandLabelShort } from '@/ui/object/object-filter-dropdown/utils/getOperandLabel';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { useViewGetStates } from '../hooks/useViewGetStates';
import SortOrFilterChip from './SortOrFilterChip';
@ -88,12 +89,18 @@ export const ViewBarDetails = ({
rightComponent,
}: ViewBarDetailsProps) => {
const {
currentViewSorts,
currentViewFilters,
canPersistFilters,
canPersistSorts,
isViewBarExpanded,
} = useViewGetStates();
currentViewSortsState,
currentViewFiltersState,
canPersistFiltersSelector,
canPersistSortsSelector,
isViewBarExpandedState,
} = useViewScopedStates();
const currentViewSorts = useRecoilValue(currentViewSortsState);
const currentViewFilters = useRecoilValue(currentViewFiltersState);
const canPersistFilters = useRecoilValue(canPersistFiltersSelector);
const canPersistSorts = useRecoilValue(canPersistSortsSelector);
const isViewBarExpanded = useRecoilValue(isViewBarExpandedState);
const { resetViewBar, removeViewSort, removeViewFilter } = useView();

View File

@ -1,66 +1,58 @@
import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback } from 'recoil';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { useFindManyObjects } from '@/metadata/hooks/useFindManyObjects';
import { PaginatedObjectTypeResults } from '@/metadata/types/PaginatedObjectTypeResults';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { assertNotNull } from '~/utils/assert';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { useViewGetStates } from '../hooks/useViewGetStates';
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { viewsScopedState } from '../states/viewsScopedState';
import { View } from '../types/View';
import { ViewField } from '../types/ViewField';
import { ViewFilter } from '../types/ViewFilter';
import { ViewSort } from '../types/ViewSort';
import { getViewScopedStatesFromSnapshot } from '../utils/getViewScopedStatesFromSnapshot';
import { getViewScopedStateValuesFromSnapshot } from '../utils/getViewScopedStateValuesFromSnapshot';
export const ViewBarEffect = () => {
const {
scopeId: viewScopeId,
setCurrentViewFields,
setSavedViewFields,
setCurrentViewFilters,
setSavedViewFilters,
setCurrentViewSorts,
setSavedViewSorts,
currentViewId,
setViews,
loadView,
changeViewInUrl,
setCurrentViewId,
} = useView();
const [searchParams] = useSearchParams();
const currentViewIdFromUrl = searchParams.get('view');
const { viewType, viewObjectId } = useViewGetStates(viewScopeId);
const { viewTypeState, viewObjectIdState } = useViewScopedStates();
const viewType = useRecoilValue(viewTypeState);
const viewObjectId = useRecoilValue(viewObjectIdState);
useFindManyObjects({
objectNamePlural: 'viewsV2',
filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } },
onCompleted: useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<View>) => {
const nextViews = data.edges.map((view) => ({
id: view.node.id,
name: view.node.name,
objectId: view.node.objectId,
}));
const views = snapshot
.getLoadable(viewsScopedState({ scopeId: viewScopeId }))
.getValue();
if (!isDeeplyEqual(views, nextViews)) setViews(nextViews);
const { viewsState } = getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId,
});
const views = getSnapshotValue(snapshot, viewsState);
if (!isDeeplyEqual(views, nextViews)) set(viewsState, nextViews);
if (!nextViews.length) return;
@ -74,43 +66,39 @@ export const ViewBarEffect = () => {
objectNamePlural: 'viewFieldsV2',
filter: { viewId: { eq: currentViewId } },
onCompleted: useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<ViewField>) => {
const availableFields = snapshot
.getLoadable(
availableFieldDefinitionsScopedState({ scopeId: viewScopeId }),
)
.getValue();
const {
availableFieldDefinitions,
onViewFieldsChange,
savedViewFields,
currentViewId,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
const onViewFieldsChange = snapshot
.getLoadable(
onViewFieldsChangeScopedState({ scopeId: viewScopeId }),
)
.getValue();
const { savedViewFieldsState, currentViewFieldsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId,
});
if (!availableFields || !currentViewId) {
if (!availableFieldDefinitions || !currentViewId) {
return;
}
const savedViewFields = snapshot
.getLoadable(
savedViewFieldsScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const queriedViewFields = data.edges
.map((viewField) => viewField.node)
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewFields, queriedViewFields)) {
setCurrentViewFields?.(queriedViewFields);
setSavedViewFields?.(queriedViewFields);
set(currentViewFieldsState, queriedViewFields);
set(savedViewFieldsState, queriedViewFields);
onViewFieldsChange?.(queriedViewFields);
}
},
[viewScopeId],
),
});
@ -119,33 +107,28 @@ export const ViewBarEffect = () => {
objectNamePlural: 'viewFiltersV2',
filter: { viewId: { eq: currentViewId } },
onCompleted: useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<Required<ViewFilter>>) => {
const availableFilterDefinitions = snapshot
.getLoadable(
availableFilterDefinitionsScopedState({ scopeId: viewScopeId }),
)
.getValue();
const {
availableFilterDefinitions,
savedViewFilters,
onViewFiltersChange,
currentViewId,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
const { savedViewFiltersState, currentViewFiltersState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId,
});
if (!availableFilterDefinitions || !currentViewId) {
return;
}
const savedViewFilters = snapshot
.getLoadable(
savedViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const onViewFiltersChange = snapshot
.getLoadable(
onViewFiltersChangeScopedState({ scopeId: viewScopeId }),
)
.getValue();
const queriedViewFilters = data.edges
.map(({ node }) => {
const availableFilterDefinition = availableFilterDefinitions.find(
@ -163,11 +146,12 @@ export const ViewBarEffect = () => {
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
setSavedViewFilters?.(queriedViewFilters);
setCurrentViewFilters?.(queriedViewFilters);
set(savedViewFiltersState, queriedViewFilters);
set(currentViewFiltersState, queriedViewFilters);
onViewFiltersChange?.(queriedViewFilters);
}
},
[viewScopeId],
),
});
@ -176,31 +160,28 @@ export const ViewBarEffect = () => {
objectNamePlural: 'viewSortsV2',
filter: { viewId: { eq: currentViewId } },
onCompleted: useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<Required<ViewSort>>) => {
const availableSortDefinitions = snapshot
.getLoadable(
availableSortDefinitionsScopedState({ scopeId: viewScopeId }),
)
.getValue();
const {
availableSortDefinitions,
savedViewSorts,
onViewSortsChange,
currentViewId,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
const { savedViewSortsState, currentViewSortsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId,
});
if (!availableSortDefinitions || !currentViewId) {
return;
}
const savedViewSorts = snapshot
.getLoadable(
savedViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const queriedViewSorts = data.edges
.map(({ node }) => {
const availableSortDefinition = availableSortDefinitions.find(
@ -219,18 +200,20 @@ export const ViewBarEffect = () => {
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
setSavedViewSorts?.(queriedViewSorts);
setCurrentViewSorts?.(queriedViewSorts);
set(savedViewSortsState, queriedViewSorts);
set(currentViewSortsState, queriedViewSorts);
onViewSortsChange?.(queriedViewSorts);
}
},
[viewScopeId],
),
});
useEffect(() => {
if (!currentViewIdFromUrl) return;
loadView(currentViewIdFromUrl);
}, [currentViewIdFromUrl, loadView, setCurrentViewId]);
}, [currentViewIdFromUrl, loadView]);
return <></>;
};

View File

@ -1,7 +1,7 @@
import { MouseEvent } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import {
IconChevronDown,
@ -22,8 +22,8 @@ import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { assertNotNull } from '~/utils/assert';
import { ViewsDropdownId } from '../constants/ViewsDropdownId';
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
import { useView } from '../hooks/useView';
import { useViewGetStates } from '../hooks/useViewGetStates';
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
font-weight: ${({ theme }) => theme.font.weight.regular};
@ -68,12 +68,17 @@ export const ViewsDropdownButton = ({
optionsDropdownScopeId,
}: ViewsDropdownButtonProps) => {
const theme = useTheme();
const { scopeId, removeView, currentViewId, changeViewInUrl } = useView();
const { removeView, changeViewInUrl } = useView();
const { views, currentView, entityCountInCurrentView } = useViewGetStates(
scopeId,
currentViewId,
const { viewsState, currentViewSelector, entityCountInCurrentViewState } =
useViewScopedStates();
const views = useRecoilValue(viewsState);
const currentView = useRecoilValue(currentViewSelector);
const entityCountInCurrentView = useRecoilValue(
entityCountInCurrentViewState,
);
const { setViewEditMode } = useView();
const {

View File

@ -0,0 +1,2 @@
// TODO: find a better pattern than using '' as a fallback
export const UNDEFINED_FAMILY_ITEM_ID = '';

View File

@ -2,37 +2,28 @@ import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { useFindOneObjectMetadataItem } from '@/metadata/hooks/useFindOneObjectMetadataItem';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { savedViewFieldByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFieldByKeyScopedFamilySelector';
import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
import { ViewField } from '@/views/types/ViewField';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
export const useViewFields = (viewScopeId: string) => {
const { updateOneMutation, createOneMutation, findManyQuery } =
useFindOneObjectMetadataItem({
objectNameSingular: 'viewFieldV2',
});
const apolloClient = useApolloClient();
const persistViewFields = useRecoilCallback(
({ snapshot }) =>
async (viewFieldsToPersist: ViewField[], viewId?: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { viewObjectId, currentViewId, savedViewFieldsByKey } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
viewId,
});
const viewObjectId = snapshot
.getLoadable(viewObjectIdScopeState({ scopeId: viewScopeId }))
.getValue();
const savedViewFieldsByKey = snapshot
.getLoadable(
savedViewFieldByKeyScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: viewId ?? currentViewId,
}),
)
.getValue();
const viewIdToPersist = viewId ?? currentViewId;
if (!currentViewId || !savedViewFieldsByKey || !viewObjectId) {
return;
@ -50,7 +41,7 @@ export const useViewFields = (viewScopeId: string) => {
variables: {
input: {
fieldId: viewField.fieldId,
viewId: viewId,
viewId: viewIdToPersist,
isVisible: viewField.isVisible,
size: viewField.size,
position: viewField.position,
@ -87,7 +78,6 @@ export const useViewFields = (viewScopeId: string) => {
const viewFieldsToCreate = viewFieldsToPersist.filter(
(viewField) => !savedViewFieldsByKey[viewField.fieldId],
);
await _createViewFields(viewFieldsToCreate);
const viewFieldsToUpdate = viewFieldsToPersist.filter(
(viewFieldToPersit) =>
@ -100,8 +90,17 @@ export const useViewFields = (viewScopeId: string) => {
viewFieldToPersit.isVisible),
);
await _createViewFields(viewFieldsToCreate);
await _updateViewFields(viewFieldsToUpdate);
},
[
apolloClient,
createOneMutation,
findManyQuery,
updateOneMutation,
viewScopeId,
],
);
return { persistViewFields };

View File

@ -4,14 +4,11 @@ import { useRecoilCallback } from 'recoil';
import { useFindOneObjectMetadataItem } from '@/metadata/hooks/useFindOneObjectMetadataItem';
import { Filter } from '@/ui/object/object-filter-dropdown/types/Filter';
import { currentViewFiltersScopedFamilyState } from '@/views/states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { onViewFiltersChangeScopedState } from '@/views/states/onViewFiltersChangeScopedState';
import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState';
import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import { ViewFilter } from '@/views/types/ViewFilter';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
import { useViewSetStates } from '../useViewSetStates';
import { useViewScopedStates } from './useViewScopedStates';
export const useViewFilters = (viewScopeId: string) => {
const {
@ -23,17 +20,27 @@ export const useViewFilters = (viewScopeId: string) => {
objectNameSingular: 'viewFilterV2',
});
const apolloClient = useApolloClient();
const { setCurrentViewFilters } = useViewSetStates(viewScopeId);
const { currentViewFiltersState } = useViewScopedStates();
const persistViewFilters = useRecoilCallback(
({ snapshot, set }) =>
async (viewId?: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, currentViewFilters, savedViewFiltersByKey } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!currentViewFilters) {
return;
}
if (!savedViewFiltersByKey) {
return;
}
const createViewFilters = (viewFiltersToCreate: ViewFilter[]) => {
if (!viewFiltersToCreate.length) return;
@ -92,31 +99,6 @@ export const useViewFilters = (viewScopeId: string) => {
);
};
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const savedViewFiltersByKey = snapshot
.getLoadable(
savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId ?? currentViewId,
}),
)
.getValue();
if (!currentViewFilters) {
return;
}
if (!savedViewFiltersByKey) {
return;
}
const filtersToCreate = currentViewFilters.filter(
(filter) => !savedViewFiltersByKey[filter.fieldId],
);
@ -158,37 +140,26 @@ export const useViewFilters = (viewScopeId: string) => {
);
const upsertViewFilter = useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
(filterToUpsert: Filter) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, savedViewFiltersByKey, onViewFiltersChange } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const savedViewFiltersByKey = snapshot
.getLoadable(
savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: currentViewId,
}),
)
.getValue();
if (!savedViewFiltersByKey) {
return;
}
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const existingSavedFilterId =
savedViewFiltersByKey[filterToUpsert.fieldId]?.id;
setCurrentViewFilters?.((filters) => {
set(currentViewFiltersState, (filters) => {
const newViewFilters = produce(filters, (filtersDraft) => {
const existingFilterIndex = filtersDraft.findIndex(
(filter) => filter.fieldId === filterToUpsert.fieldId,
@ -211,38 +182,29 @@ export const useViewFilters = (viewScopeId: string) => {
return newViewFilters;
});
},
[currentViewFiltersState, viewScopeId],
);
const removeViewFilter = useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
(fieldId: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, currentViewFilters, onViewFiltersChange } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const newViewFilters = currentViewFilters.filter((filter) => {
return filter.fieldId !== fieldId;
});
setCurrentViewFilters?.(newViewFilters);
set(currentViewFiltersState, newViewFilters);
onViewFiltersChange?.(newViewFilters);
},
[currentViewFiltersState, viewScopeId],
);
return { persistViewFilters, removeViewFilter, upsertViewFilter };

View File

@ -0,0 +1,84 @@
import { useRecoilState } from 'recoil';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
import { UNDEFINED_FAMILY_ITEM_ID } from '../../constants';
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewIdScopedState } from '../../states/currentViewIdScopedState';
import { getViewScopedStates } from '../../utils/internal/getViewScopedStates';
export const useViewScopedStates = (args?: { customViewScopeId?: string }) => {
const { customViewScopeId } = args ?? {};
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
customViewScopeId,
);
// View
const [currentViewId] = useRecoilState(
getScopedState(currentViewIdScopedState, scopeId),
);
const viewId = currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
viewsState,
} = getViewScopedStates({
viewScopeId: scopeId,
viewId,
});
return {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
viewsState,
};
};

View File

@ -4,14 +4,11 @@ import { useRecoilCallback } from 'recoil';
import { useFindOneObjectMetadataItem } from '@/metadata/hooks/useFindOneObjectMetadataItem';
import { Sort } from '@/ui/object/object-sort-dropdown/types/Sort';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState';
import { onViewSortsChangeScopedState } from '@/views/states/onViewSortsChangeScopedState';
import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState';
import { savedViewSortsByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewSortsByKeyScopedFamilySelector';
import { ViewSort } from '@/views/types/ViewSort';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
import { useViewSetStates } from '../useViewSetStates';
import { useViewScopedStates } from './useViewScopedStates';
export const useViewSorts = (viewScopeId: string) => {
const {
@ -23,18 +20,29 @@ export const useViewSorts = (viewScopeId: string) => {
objectNameSingular: 'viewSortV2',
});
const apolloClient = useApolloClient();
const { setCurrentViewSorts } = useViewSetStates(viewScopeId);
const { currentViewSortsState } = useViewScopedStates();
const persistViewSorts = useRecoilCallback(
({ snapshot, set }) =>
async (viewId?: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, currentViewSorts, savedViewSortsByKey } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!currentViewSorts) {
return;
}
if (!savedViewSortsByKey) {
return;
}
const createViewSorts = (viewSortsToCreate: ViewSort[]) => {
if (!viewSortsToCreate.length) return;
@ -88,31 +96,6 @@ export const useViewSorts = (viewScopeId: string) => {
);
};
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const savedViewSortsByKey = snapshot
.getLoadable(
savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId ?? currentViewId,
}),
)
.getValue();
if (!currentViewSorts) {
return;
}
if (!savedViewSortsByKey) {
return;
}
const sortsToCreate = currentViewSorts.filter(
(sort) => !savedViewSortsByKey[sort.fieldId],
);
@ -153,37 +136,26 @@ export const useViewSorts = (viewScopeId: string) => {
);
const upsertViewSort = useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
(sortToUpsert: Sort) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, onViewSortsChange, savedViewSortsByKey } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const savedViewSortsByKey = snapshot
.getLoadable(
savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: currentViewId,
}),
)
.getValue();
if (!savedViewSortsByKey) {
return;
}
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const existingSavedSortId =
savedViewSortsByKey[sortToUpsert.fieldId]?.id;
setCurrentViewSorts?.((sorts) => {
set(currentViewSortsState, (sorts) => {
const newViewSorts = produce(sorts, (sortsDraft) => {
const existingSortIndex = sortsDraft.findIndex(
(sort) => sort.fieldId === sortToUpsert.fieldId,
@ -203,38 +175,29 @@ export const useViewSorts = (viewScopeId: string) => {
return newViewSorts;
});
},
[currentViewSortsState, viewScopeId],
);
const removeViewSort = useRecoilCallback(
({ snapshot }) =>
({ snapshot, set }) =>
(fieldId: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
const { currentViewId, onViewSortsChange, currentViewSorts } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const newViewSorts = currentViewSorts.filter((filter) => {
return filter.fieldId !== fieldId;
});
setCurrentViewSorts?.(newViewSorts);
set(currentViewSortsState, newViewSorts);
onViewSortsChange?.(newViewSorts);
},
[currentViewSortsState, viewScopeId],
);
return { persistViewSorts, upsertViewSort, removeViewSort };

View File

@ -2,9 +2,8 @@ import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { useFindOneObjectMetadataItem } from '@/metadata/hooks/useFindOneObjectMetadataItem';
import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
import { viewTypeScopedState } from '@/views/states/viewTypeScopedState';
import { View } from '@/views/types/View';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
export const useViews = (scopeId: string) => {
const {
@ -20,13 +19,12 @@ export const useViews = (scopeId: string) => {
const createView = useRecoilCallback(
({ snapshot }) =>
async (view: Pick<View, 'id' | 'name'>) => {
const viewObjectId = await snapshot
.getLoadable(viewObjectIdScopeState({ scopeId }))
.getValue();
const viewType = await snapshot
.getLoadable(viewTypeScopedState({ scopeId }))
.getValue();
const { viewObjectId, viewType } = getViewScopedStateValuesFromSnapshot(
{
snapshot,
viewScopeId: scopeId,
},
);
if (!viewObjectId || !viewType) {
return;
@ -43,6 +41,7 @@ export const useViews = (scopeId: string) => {
refetchQueries: [findManyQuery],
});
},
[scopeId, apolloClient, createOneMutation, findManyQuery],
);
const updateView = async (view: View) => {

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback } from 'recoil';
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
import { v4 } from 'uuid';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
@ -8,22 +8,15 @@ import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-i
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector';
import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
import { viewsScopedState } from '../states/viewsScopedState';
import { getViewScopedStatesFromSnapshot } from '../utils/getViewScopedStatesFromSnapshot';
import { getViewScopedStateValuesFromSnapshot } from '../utils/getViewScopedStateValuesFromSnapshot';
import { useViewFields } from './internal/useViewFields';
import { useViewFilters } from './internal/useViewFilters';
import { useViews } from './internal/useViews';
import { useViewScopedStates } from './internal/useViewScopedStates';
import { useViewSorts } from './internal/useViewSorts';
import { useViewSetStates } from './useViewSetStates';
type UseViewProps = {
viewScopeId?: string;
@ -36,32 +29,19 @@ export const useView = (props?: UseViewProps) => {
);
const {
setCurrentViewId,
currentViewId,
setViews,
setViewEditMode,
setViewObjectId,
setViewType,
setEntityCountInCurrentView,
setIsViewBarExpanded,
setAvailableSortDefinitions,
setCurrentViewSorts,
setSavedViewSorts,
setAvailableFilterDefinitions,
setCurrentViewFilters,
setSavedViewFilters,
setAvailableFieldDefinitions,
setCurrentViewFields,
setSavedViewFields,
setOnViewFieldsChange,
setOnViewFiltersChange,
setOnViewSortsChange,
} = useViewSetStates(scopeId);
currentViewFiltersState,
currentViewIdState,
currentViewSortsState,
viewEditModeState,
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
entityCountInCurrentViewState,
viewObjectIdState,
viewTypeState,
} = useViewScopedStates({
customViewScopeId: scopeId,
});
const { persistViewSorts, upsertViewSort, removeViewSort } =
useViewSorts(scopeId);
@ -73,6 +53,28 @@ export const useView = (props?: UseViewProps) => {
updateView: internalUpdateView,
deleteView: internalDeleteView,
} = useViews(scopeId);
const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState);
const setAvailableFieldDefinitions = useSetRecoilState(
availableFieldDefinitionsState,
);
const setAvailableSortDefinitions = useSetRecoilState(
availableSortDefinitionsState,
);
const setAvailableFilterDefinitions = useSetRecoilState(
availableFilterDefinitionsState,
);
const setEntityCountInCurrentView = useSetRecoilState(
entityCountInCurrentViewState,
);
const setViewEditMode = useSetRecoilState(viewEditModeState);
const setViewObjectId = useSetRecoilState(viewObjectIdState);
const setViewType = useSetRecoilState(viewTypeState);
const [_, setSearchParams] = useSearchParams();
const changeViewInUrl = useCallback(
@ -82,72 +84,62 @@ export const useView = (props?: UseViewProps) => {
[setSearchParams],
);
const loadView = useRecoilCallback(({ snapshot }) => (viewId: string) => {
setCurrentViewId?.(viewId);
const currentViewFields = snapshot
.getLoadable(
currentViewFieldsScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const loadView = useRecoilCallback(
({ snapshot }) =>
(viewId: string) => {
setCurrentViewId?.(viewId);
const onViewFieldsChange = snapshot
.getLoadable(onViewFieldsChangeScopedState({ scopeId }))
.getValue();
const {
currentViewFields,
onViewFieldsChange,
currentViewFilters,
onViewFiltersChange,
currentViewSorts,
onViewSortsChange,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId,
});
onViewFieldsChange?.(currentViewFields);
onViewFieldsChange?.(currentViewFields);
onViewFiltersChange?.(currentViewFilters);
onViewSortsChange?.(currentViewSorts);
},
[setCurrentViewId, scopeId],
);
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const resetViewBar = useRecoilCallback(
({ snapshot, set }) =>
() => {
const {
savedViewFilters,
savedViewSorts,
onViewFiltersChange,
onViewSortsChange,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId }))
.getValue();
if (savedViewFilters) {
set(currentViewFiltersState, savedViewFilters);
onViewFiltersChange?.(savedViewFilters);
}
if (savedViewSorts) {
set(currentViewSortsState, savedViewSorts);
onViewSortsChange?.(savedViewSorts);
}
onViewFiltersChange?.(currentViewFilters);
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId }))
.getValue();
onViewSortsChange?.(currentViewSorts);
});
const resetViewBar = useRecoilCallback(({ snapshot }) => () => {
const savedViewFilters = snapshot
.getLoadable(
savedViewFiltersScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
const savedViewSorts = snapshot
.getLoadable(
savedViewSortsScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
if (savedViewFilters) {
setCurrentViewFilters?.(savedViewFilters);
}
if (savedViewSorts) {
setCurrentViewSorts?.(savedViewSorts);
}
setViewEditMode?.('none');
});
set(viewEditModeState, 'none');
},
[
currentViewFiltersState,
currentViewSortsState,
scopeId,
viewEditModeState,
],
);
const createView = useRecoilCallback(
({ snapshot, set }) =>
@ -155,29 +147,17 @@ export const useView = (props?: UseViewProps) => {
const newViewId = v4();
await internalCreateView({ id: newViewId, name });
const currentViewFields = snapshot
.getLoadable(
currentViewFieldsScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
const { currentViewFields, currentViewFilters, currentViewSorts } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
set(
currentViewFieldsScopedFamilyState({ scopeId, familyKey: newViewId }),
currentViewFields,
);
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
set(
currentViewFiltersScopedFamilyState({
scopeId,
@ -186,15 +166,6 @@ export const useView = (props?: UseViewProps) => {
currentViewFilters,
);
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
set(
currentViewSortsScopedFamilyState({
scopeId,
@ -211,7 +182,6 @@ export const useView = (props?: UseViewProps) => {
},
[
changeViewInUrl,
currentViewId,
internalCreateView,
persistViewFields,
persistViewFilters,
@ -228,19 +198,30 @@ export const useView = (props?: UseViewProps) => {
const removeView = useRecoilCallback(
({ set, snapshot }) =>
async (viewIdToDelete: string) => {
const currentViewId = await snapshot.getPromise(
currentViewIdScopedState({ scopeId }),
);
const { currentViewId } = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (currentViewId === viewIdToDelete)
set(currentViewIdScopedState({ scopeId }), undefined);
const { currentViewIdState, viewsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
set(viewsScopedState({ scopeId }), (previousViews) =>
if (currentViewId === viewIdToDelete) {
set(currentViewIdState, undefined);
}
set(viewsState, (previousViews) =>
previousViews.filter((view) => view.id !== viewIdToDelete),
);
internalDeleteView(viewIdToDelete);
if (currentViewId === viewIdToDelete) setSearchParams();
if (currentViewId === viewIdToDelete) {
setSearchParams();
}
},
[internalDeleteView, scopeId, setSearchParams],
);
@ -252,13 +233,11 @@ export const useView = (props?: UseViewProps) => {
return;
}
const viewEditMode = snapshot
.getLoadable(viewEditModeScopedState({ scopeId }))
.getValue();
const currentView = snapshot
.getLoadable(currentViewScopedSelector(scopeId))
.getValue();
const { viewEditMode, currentView } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (!currentView) {
return;
@ -284,38 +263,25 @@ export const useView = (props?: UseViewProps) => {
updateCurrentView,
createView,
removeView,
setIsViewBarExpanded,
resetViewBar,
handleViewNameSubmit,
setViews,
setViewEditMode,
setViewObjectId,
setViewType,
setEntityCountInCurrentView,
setAvailableFieldDefinitions,
setAvailableSortDefinitions,
setCurrentViewSorts,
setSavedViewSorts,
upsertViewSort,
removeViewSort,
setAvailableFilterDefinitions,
setCurrentViewFilters,
setSavedViewFilters,
upsertViewFilter,
removeViewFilter,
setAvailableFieldDefinitions,
setCurrentViewFields,
setSavedViewFields,
persistViewFields,
changeViewInUrl,
loadView,
setOnViewFieldsChange,
setOnViewFiltersChange,
setOnViewSortsChange,
};
};

View File

@ -1,211 +0,0 @@
import { useRecoilValue } from 'recoil';
import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState';
import { isViewBarExpandedScopedState } from '../states/isViewBarExpandedScopedState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { canPersistViewFiltersScopedFamilySelector } from '../states/selectors/canPersistViewFiltersScopedFamilySelector';
import { canPersistViewSortsScopedFamilySelector } from '../states/selectors/canPersistViewSortsScopedFamilySelector';
import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector';
import { savedViewFieldByKeyScopedFamilySelector } from '../states/selectors/savedViewFieldByKeyScopedFamilySelector';
import { savedViewFiltersByKeyScopedFamilySelector } from '../states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import { savedViewSortsByKeyScopedFamilySelector } from '../states/selectors/savedViewSortsByKeyScopedFamilySelector';
import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
import { viewsScopedState } from '../states/viewsScopedState';
import { viewTypeScopedState } from '../states/viewTypeScopedState';
export const useViewGetStates = (viewScopeId?: string, viewId?: string) => {
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
viewScopeId,
);
// View
const [currentViewId] = useRecoilScopedStateV2(
currentViewIdScopedState,
scopeId,
);
const familyItemId = viewId ?? currentViewId;
const currentView = useRecoilValue(currentViewScopedSelector(scopeId));
const [viewEditMode] = useRecoilScopedStateV2(
viewEditModeScopedState,
scopeId,
);
const [views] = useRecoilScopedStateV2(viewsScopedState, scopeId);
const [viewObjectId] = useRecoilScopedStateV2(
viewObjectIdScopeState,
scopeId,
);
const [viewType] = useRecoilScopedStateV2(viewTypeScopedState, scopeId);
const [entityCountInCurrentView] = useRecoilScopedStateV2(
entityCountInCurrentViewScopedState,
scopeId,
);
const [isViewBarExpanded] = useRecoilScopedStateV2(
isViewBarExpandedScopedState,
scopeId,
);
// ViewSorts
const [currentViewSorts] = useRecoilScopedFamilyState(
currentViewSortsScopedFamilyState,
scopeId,
familyItemId,
);
const [savedViewSorts] = useRecoilScopedFamilyState(
savedViewSortsScopedFamilyState,
scopeId,
familyItemId,
);
const savedViewSortsByKey = useRecoilValue(
savedViewSortsByKeyScopedFamilySelector({
scopeId: scopeId,
viewId: familyItemId,
}),
);
const [availableSortDefinitions] = useRecoilScopedStateV2(
availableSortDefinitionsScopedState,
scopeId,
);
const canPersistSorts = useRecoilValue(
canPersistViewSortsScopedFamilySelector({
viewScopeId: scopeId,
viewId: familyItemId,
}),
);
// ViewFilters
const [currentViewFilters] = useRecoilScopedFamilyState(
currentViewFiltersScopedFamilyState,
scopeId,
familyItemId,
);
const [savedViewFilters] = useRecoilScopedFamilyState(
savedViewFiltersScopedFamilyState,
scopeId,
familyItemId,
);
const savedViewFiltersByKey = useRecoilValue(
savedViewFiltersByKeyScopedFamilySelector({
scopeId: scopeId,
viewId: familyItemId,
}),
);
const [availableFilterDefinitions] = useRecoilScopedStateV2(
availableFilterDefinitionsScopedState,
scopeId,
);
const canPersistFilters = useRecoilValue(
canPersistViewFiltersScopedFamilySelector({
viewScopeId: scopeId,
viewId: familyItemId,
}),
);
// ViewFields
const [availableFieldDefinitions] = useRecoilScopedStateV2(
availableFieldDefinitionsScopedState,
scopeId,
);
const [currentViewFields] = useRecoilScopedFamilyState(
currentViewFieldsScopedFamilyState,
scopeId,
familyItemId,
);
const [savedViewFields] = useRecoilScopedFamilyState(
savedViewFieldsScopedFamilyState,
scopeId,
familyItemId,
);
const savedViewFieldsByKey = useRecoilValue(
savedViewFieldByKeyScopedFamilySelector({
viewScopeId: scopeId,
viewId: familyItemId,
}),
);
// ViewChangeHandlers
const [onViewSortsChange] = useRecoilScopedStateV2(
onViewSortsChangeScopedState,
scopeId,
);
const [onViewFiltersChange] = useRecoilScopedStateV2(
onViewFiltersChangeScopedState,
scopeId,
);
const [onViewFieldsChange] = useRecoilScopedStateV2(
onViewFieldsChangeScopedState,
scopeId,
);
return {
currentViewId,
currentView,
isViewBarExpanded,
views,
viewEditMode,
viewObjectId,
viewType,
entityCountInCurrentView,
availableSortDefinitions,
currentViewSorts,
savedViewSorts,
savedViewSortsByKey,
canPersistSorts,
availableFilterDefinitions,
currentViewFilters,
savedViewFilters,
savedViewFiltersByKey,
canPersistFilters,
availableFieldDefinitions,
currentViewFields,
savedViewFieldsByKey,
savedViewFields,
onViewSortsChange,
onViewFiltersChange,
onViewFieldsChange,
};
};

View File

@ -1,160 +0,0 @@
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { useSetRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedFamilyState';
import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState';
import { isViewBarExpandedScopedState } from '../states/isViewBarExpandedScopedState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
import { viewsScopedState } from '../states/viewsScopedState';
import { viewTypeScopedState } from '../states/viewTypeScopedState';
export const useViewSetStates = (viewScopeId?: string, viewId?: string) => {
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
viewScopeId,
);
// View
const [currentViewId, setCurrentViewId] = useRecoilScopedStateV2(
currentViewIdScopedState,
scopeId,
);
const setViewObjectId = useSetRecoilScopedStateV2(
viewObjectIdScopeState,
scopeId,
);
const setViewType = useSetRecoilScopedStateV2(viewTypeScopedState, scopeId);
const familyItemId = viewId ? viewId : currentViewId;
const setViewEditMode = useSetRecoilScopedStateV2(
viewEditModeScopedState,
scopeId,
);
const setViews = useSetRecoilScopedStateV2(viewsScopedState, scopeId);
const setEntityCountInCurrentView = useSetRecoilScopedStateV2(
entityCountInCurrentViewScopedState,
scopeId,
);
const setIsViewBarExpanded = useSetRecoilScopedStateV2(
isViewBarExpandedScopedState,
scopeId,
);
// ViewSorts
const setCurrentViewSorts = useSetRecoilScopedFamilyState(
currentViewSortsScopedFamilyState,
scopeId,
familyItemId,
);
const setSavedViewSorts = useSetRecoilScopedFamilyState(
savedViewSortsScopedFamilyState,
scopeId,
familyItemId,
);
const setAvailableSortDefinitions = useSetRecoilScopedStateV2(
availableSortDefinitionsScopedState,
scopeId,
);
// ViewFilters
const setCurrentViewFilters = useSetRecoilScopedFamilyState(
currentViewFiltersScopedFamilyState,
scopeId,
familyItemId,
);
const setSavedViewFilters = useSetRecoilScopedFamilyState(
savedViewFiltersScopedFamilyState,
scopeId,
familyItemId,
);
const setAvailableFilterDefinitions = useSetRecoilScopedStateV2(
availableFilterDefinitionsScopedState,
scopeId,
);
// ViewFields
const setAvailableFieldDefinitions = useSetRecoilScopedStateV2(
availableFieldDefinitionsScopedState,
scopeId,
);
const setCurrentViewFields = useSetRecoilScopedFamilyState(
currentViewFieldsScopedFamilyState,
scopeId,
familyItemId,
);
const setSavedViewFields = useSetRecoilScopedFamilyState(
savedViewFieldsScopedFamilyState,
scopeId,
familyItemId,
);
const setOnViewFieldsChange = useSetRecoilScopedStateV2(
onViewFieldsChangeScopedState,
scopeId,
);
const setOnViewFiltersChange = useSetRecoilScopedStateV2(
onViewFiltersChangeScopedState,
scopeId,
);
const setOnViewSortsChange = useSetRecoilScopedStateV2(
onViewSortsChangeScopedState,
scopeId,
);
return {
currentViewId,
setCurrentViewId,
setIsViewBarExpanded,
setViewObjectId,
setViewType,
setViews,
setViewEditMode,
setEntityCountInCurrentView,
setAvailableSortDefinitions,
setCurrentViewSorts,
setSavedViewSorts,
setAvailableFilterDefinitions,
setCurrentViewFilters,
setSavedViewFilters,
setAvailableFieldDefinitions,
setCurrentViewFields,
setSavedViewFields,
setOnViewFieldsChange,
setOnViewFiltersChange,
setOnViewSortsChange,
};
};

View File

@ -1,8 +1,9 @@
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { Filter } from '@/ui/object/object-filter-dropdown/types/Filter';
import { Sort } from '@/ui/object/object-sort-dropdown/types/Sort';
import { useView } from '@/views/hooks/useView';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { ViewField } from '@/views/types/ViewField';
type ViewScopeInitEffectProps = {
@ -18,10 +19,14 @@ export const ViewScopeInitEffect = ({
onViewFieldsChange,
}: ViewScopeInitEffectProps) => {
const {
setOnViewSortsChange,
setOnViewFieldsChange,
setOnViewFiltersChange,
} = useView();
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
} = useViewScopedStates();
const setOnViewSortsChange = useSetRecoilState(onViewSortsChangeState);
const setOnViewFiltersChange = useSetRecoilState(onViewFiltersChangeState);
const setOnViewFieldsChange = useSetRecoilState(onViewFieldsChangeState);
useEffect(() => {
setOnViewSortsChange(() => onViewSortsChange);

View File

@ -1,22 +1,23 @@
import { selectorFamily } from 'recoil';
import { createScopedSelector } from '@/ui/utilities/recoil-scope/utils/createScopedSelector';
import { View } from '@/views/types/View';
import { currentViewIdScopedState } from '../currentViewIdScopedState';
import { viewsByIdScopedSelector } from './viewsByIdScopedSelector';
export const currentViewScopedSelector = selectorFamily<
View | undefined,
string
>({
key: 'currentViewScopedSelector',
get:
(scopeId) =>
({ get }) => {
const currentViewId = get(currentViewIdScopedState({ scopeId: scopeId }));
return currentViewId
? get(viewsByIdScopedSelector(scopeId))[currentViewId]
: undefined;
},
});
export const currentViewScopedSelector = createScopedSelector<View | undefined>(
{
key: 'currentViewScopedSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
const currentViewId = get(
currentViewIdScopedState({ scopeId: scopeId }),
);
return currentViewId
? get(viewsByIdScopedSelector(scopeId))[currentViewId]
: undefined;
},
},
);

View File

@ -18,6 +18,7 @@ export const savedViewFieldByKeyScopedFamilySelector = selectorFamily({
if (viewId === undefined) {
return undefined;
}
return get(
savedViewFieldsScopedFamilyState({
scopeId: viewScopeId,

View File

@ -0,0 +1,106 @@
import { Snapshot } from 'recoil';
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { UNDEFINED_FAMILY_ITEM_ID } from '../constants';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { getViewScopedStates } from './internal/getViewScopedStates';
export const getViewScopedStateValuesFromSnapshot = ({
snapshot,
viewScopeId,
viewId,
}: {
snapshot: Snapshot;
viewScopeId: string;
viewId?: string;
}) => {
const currentViewId = getSnapshotValue(
snapshot,
getScopedState(currentViewIdScopedState, viewScopeId),
);
const familyItemId = viewId ?? currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
viewsState,
} = getViewScopedStates({
viewScopeId,
viewId: familyItemId,
});
return {
availableFieldDefinitions: getSnapshotValue(
snapshot,
availableFieldDefinitionsState,
),
availableFilterDefinitions: getSnapshotValue(
snapshot,
availableFilterDefinitionsState,
),
availableSortDefinitions: getSnapshotValue(
snapshot,
availableSortDefinitionsState,
),
canPersistFilters: getSnapshotValue(snapshot, canPersistFiltersSelector),
canPersistSorts: getSnapshotValue(snapshot, canPersistSortsSelector),
currentViewFields: getSnapshotValue(snapshot, currentViewFieldsState),
currentViewFilters: getSnapshotValue(snapshot, currentViewFiltersState),
currentViewId: getSnapshotValue(snapshot, currentViewIdState),
currentView: getSnapshotValue(snapshot, currentViewSelector),
currentViewSorts: getSnapshotValue(snapshot, currentViewSortsState),
entityCountInCurrentView: getSnapshotValue(
snapshot,
entityCountInCurrentViewState,
),
isViewBarExpanded: getSnapshotValue(snapshot, isViewBarExpandedState),
onViewFieldsChange: getSnapshotValue(snapshot, onViewFieldsChangeState),
onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState),
onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState),
savedViewFieldsByKey: getSnapshotValue(
snapshot,
savedViewFieldsByKeySelector,
),
savedViewFields: getSnapshotValue(snapshot, savedViewFieldsState),
savedViewFiltersByKey: getSnapshotValue(
snapshot,
savedViewFiltersByKeySelector,
),
savedViewFilters: getSnapshotValue(snapshot, savedViewFiltersState),
savedViewSortsByKey: getSnapshotValue(
snapshot,
savedViewSortsByKeySelector,
),
savedViewSorts: getSnapshotValue(snapshot, savedViewSortsState),
viewEditMode: getSnapshotValue(snapshot, viewEditModeState),
viewObjectId: getSnapshotValue(snapshot, viewObjectIdState),
viewType: getSnapshotValue(snapshot, viewTypeState),
views: getSnapshotValue(snapshot, viewsState),
};
};

View File

@ -0,0 +1,85 @@
import { Snapshot } from 'recoil';
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { UNDEFINED_FAMILY_ITEM_ID } from '../constants';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { getViewScopedStates } from './internal/getViewScopedStates';
export const getViewScopedStatesFromSnapshot = ({
snapshot,
viewScopeId,
viewId,
}: {
snapshot: Snapshot;
viewScopeId: string;
viewId?: string;
}) => {
const currentViewId = getSnapshotValue(
snapshot,
getScopedState(currentViewIdScopedState, viewScopeId),
);
const usedViewId = viewId ?? currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector: canPersistFiltersSelector,
canPersistSortsSelector: canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector: savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector: savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector: savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
viewsState,
} = getViewScopedStates({
viewScopeId,
viewId: usedViewId,
});
return {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersReadOnlyState: canPersistFiltersSelector,
canPersistSortsReadOnlyState: canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeyReadOnlyState: savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeyReadOnlyState: savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeyReadOnlyState: savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
viewsState,
};
};

View File

@ -0,0 +1,198 @@
import { getScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/getScopedFamilyState';
import { getScopedSelector } from '@/ui/utilities/recoil-scope/utils/getScopedSelector';
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { currentViewScopedSelector } from '@/views/states/selectors/currentViewScopedSelector';
import { availableFieldDefinitionsScopedState } from '../../states/availableFieldDefinitionsScopedState';
import { availableFilterDefinitionsScopedState } from '../../states/availableFilterDefinitionsScopedState';
import { availableSortDefinitionsScopedState } from '../../states/availableSortDefinitionsScopedState';
import { currentViewFieldsScopedFamilyState } from '../../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../../states/currentViewFiltersScopedFamilyState';
import { currentViewSortsScopedFamilyState } from '../../states/currentViewSortsScopedFamilyState';
import { entityCountInCurrentViewScopedState } from '../../states/entityCountInCurrentViewScopedState';
import { isViewBarExpandedScopedState } from '../../states/isViewBarExpandedScopedState';
import { onViewFieldsChangeScopedState } from '../../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../../states/onViewSortsChangeScopedState';
import { savedViewFieldsScopedFamilyState } from '../../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../../states/savedViewSortsScopedFamilyState';
import { canPersistViewFiltersScopedFamilySelector } from '../../states/selectors/canPersistViewFiltersScopedFamilySelector';
import { canPersistViewSortsScopedFamilySelector } from '../../states/selectors/canPersistViewSortsScopedFamilySelector';
import { savedViewFieldByKeyScopedFamilySelector } from '../../states/selectors/savedViewFieldByKeyScopedFamilySelector';
import { savedViewFiltersByKeyScopedFamilySelector } from '../../states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import { savedViewSortsByKeyScopedFamilySelector } from '../../states/selectors/savedViewSortsByKeyScopedFamilySelector';
import { viewEditModeScopedState } from '../../states/viewEditModeScopedState';
import { viewObjectIdScopeState } from '../../states/viewObjectIdScopeState';
import { viewsScopedState } from '../../states/viewsScopedState';
import { viewTypeScopedState } from '../../states/viewTypeScopedState';
export const getViewScopedStates = ({
viewScopeId,
viewId,
}: {
viewScopeId: string;
viewId: string;
}) => {
const viewEditModeState = getScopedState(
viewEditModeScopedState,
viewScopeId,
);
const viewsState = getScopedState(viewsScopedState, viewScopeId);
const viewObjectIdState = getScopedState(viewObjectIdScopeState, viewScopeId);
const viewTypeState = getScopedState(viewTypeScopedState, viewScopeId);
const entityCountInCurrentViewState = getScopedState(
entityCountInCurrentViewScopedState,
viewScopeId,
);
const isViewBarExpandedState = getScopedState(
isViewBarExpandedScopedState,
viewScopeId,
);
// ViewSorts
const currentViewSortsState = getScopedFamilyState(
currentViewSortsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewSortsState = getScopedFamilyState(
savedViewSortsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewSortsByKeySelector = savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId,
});
const availableSortDefinitionsState = getScopedState(
availableSortDefinitionsScopedState,
viewScopeId,
);
const canPersistSortsSelector = canPersistViewSortsScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: viewId,
});
// ViewFilters
const currentViewFiltersState = getScopedFamilyState(
currentViewFiltersScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFiltersState = getScopedFamilyState(
savedViewFiltersScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFiltersByKeySelector =
savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId,
});
const availableFilterDefinitionsState = getScopedState(
availableFilterDefinitionsScopedState,
viewScopeId,
);
const canPersistFiltersSelector = canPersistViewFiltersScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: viewId,
});
// ViewFields
const availableFieldDefinitionsState = getScopedState(
availableFieldDefinitionsScopedState,
viewScopeId,
);
const currentViewFieldsState = getScopedFamilyState(
currentViewFieldsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFieldsState = getScopedFamilyState(
savedViewFieldsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFieldsByKeySelector = savedViewFieldByKeyScopedFamilySelector({
viewScopeId,
viewId,
});
// ViewChangeHandlers
const onViewSortsChangeState = getScopedState(
onViewSortsChangeScopedState,
viewScopeId,
);
const onViewFiltersChangeState = getScopedState(
onViewFiltersChangeScopedState,
viewScopeId,
);
const onViewFieldsChangeState = getScopedState(
onViewFieldsChangeScopedState,
viewScopeId,
);
const currentViewIdState = getScopedState(
currentViewIdScopedState,
viewScopeId,
);
const currentViewSelector = getScopedSelector(
currentViewScopedSelector,
viewScopeId,
);
return {
currentViewIdState,
currentViewSelector,
isViewBarExpandedState,
viewsState,
viewEditModeState,
viewObjectIdState,
viewTypeState,
entityCountInCurrentViewState,
availableSortDefinitionsState,
currentViewSortsState,
savedViewSortsState,
savedViewSortsByKeySelector,
canPersistSortsSelector,
availableFilterDefinitionsState,
currentViewFiltersState,
savedViewFiltersState,
savedViewFiltersByKeySelector,
canPersistFiltersSelector,
availableFieldDefinitionsState,
currentViewFieldsState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
onViewSortsChangeState,
onViewFiltersChangeState,
onViewFieldsChangeState,
};
};

View File

@ -3,7 +3,7 @@ import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinitio
import { ViewField } from '../types/ViewField';
export const columnDefinitionsToViewFields = (
export const mapColumnDefinitionsToViewFields = (
columnDefinitions: ColumnDefinition<FieldMetadata>[],
): ViewField[] => {
return columnDefinitions.map((columnDefinition) => ({

View File

@ -4,7 +4,7 @@ import { assertNotNull } from '~/utils/assert';
import { ViewField } from '../types/ViewField';
export const viewFieldsToBoardFieldDefinitions = (
export const mapViewFieldsToBoardFieldDefinitions = (
viewFields: ViewField[],
fieldsMetadata: BoardFieldDefinition<FieldMetadata>[],
): BoardFieldDefinition<FieldMetadata>[] => {

View File

@ -4,7 +4,7 @@ import { assertNotNull } from '~/utils/assert';
import { ViewField } from '../types/ViewField';
export const viewFieldsToColumnDefinitions = (
export const mapViewFieldsToColumnDefinitions = (
viewFields: ViewField[],
fieldsMetadata: ColumnDefinition<FieldMetadata>[],
): ColumnDefinition<FieldMetadata>[] => {

View File

@ -2,7 +2,9 @@ import { Filter } from '@/ui/object/object-filter-dropdown/types/Filter';
import { ViewFilter } from '../types/ViewFilter';
export const viewFiltersToFilters = (viewFilters: ViewFilter[]): Filter[] => {
export const mapViewFiltersToFilters = (
viewFilters: ViewFilter[],
): Filter[] => {
return viewFilters.map((viewFilter) => {
return {
fieldId: viewFilter.fieldId,

View File

@ -2,7 +2,7 @@ import { Sort } from '@/ui/object/object-sort-dropdown/types/Sort';
import { ViewSort } from '../types/ViewSort';
export const viewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => {
export const mapViewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => {
return viewSorts.map((viewSort) => {
return {
fieldId: viewSort.fieldId,

View File

@ -38,7 +38,7 @@ export const Email: Story = {
await canvas.findByText(
mockedPeopleData[0].displayName,
{},
{ timeout: 3000 },
{ timeout: 1000 },
);
});
@ -82,7 +82,7 @@ export const Reset: Story = {
await canvas.findByText(
mockedPeopleData[0].displayName,
{},
{ timeout: 3000 },
{ timeout: 1000 },
);
});