Refactor Views by cleaning the code, relying on apolloCache and improving performances (#4516)

* Wip refactoring view

* Post merge conflicts

* Fix review

* Add create view capability

* Fix create object missing view

* Fix tests
This commit is contained in:
Charles Bochet
2024-03-20 14:21:58 +01:00
committed by GitHub
parent 20e14cb455
commit cfb0cce9b8
392 changed files with 3474 additions and 4410 deletions

View File

@ -12,7 +12,7 @@ import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
const baseDefinition = {
fieldMetadataId: 'fieldMetadataId',
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
label: 'label',
iconName: 'iconName',
};
@ -21,20 +21,22 @@ describe('mapViewSortsToSorts', () => {
it('should map each ViewSort object to a corresponding Sort object', () => {
const viewSorts: ViewSort[] = [
{
__typename: 'ViewSort',
id: 'id',
fieldMetadataId: 'fieldMetadataId',
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
direction: 'asc',
definition: baseDefinition,
},
];
const expectedSorts: Sort[] = [
{
fieldMetadataId: 'fieldMetadataId',
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
direction: 'asc',
definition: baseDefinition,
},
];
expect(mapViewSortsToSorts(viewSorts)).toEqual(expectedSorts);
expect(mapViewSortsToSorts(viewSorts, [baseDefinition])).toEqual(
expectedSorts,
);
});
});
@ -42,20 +44,17 @@ describe('mapViewFiltersToFilters', () => {
it('should map each ViewFilter object to a corresponding Filter object', () => {
const viewFilters: ViewFilter[] = [
{
__typename: 'ViewFilter',
id: 'id',
fieldMetadataId: '1',
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
value: 'testValue',
displayValue: 'Test Display Value',
operand: ViewFilterOperand.Is,
definition: {
...baseDefinition,
type: 'FULL_NAME',
},
},
];
const expectedFilters: Filter[] = [
{
fieldMetadataId: '1',
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
value: 'testValue',
displayValue: 'Test Display Value',
operand: ViewFilterOperand.Is,
@ -65,7 +64,14 @@ describe('mapViewFiltersToFilters', () => {
},
},
];
expect(mapViewFiltersToFilters(viewFilters)).toEqual(expectedFilters);
expect(
mapViewFiltersToFilters(viewFilters, [
{
...baseDefinition,
type: 'FULL_NAME',
},
]),
).toEqual(expectedFilters);
});
});
@ -73,6 +79,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
it('should map visible ViewFields to ColumnDefinitions and filter out missing fieldMetadata', () => {
const viewFields: ViewField[] = [
{
__typename: 'ViewField',
id: '1',
fieldMetadataId: '1',
position: 1,
@ -92,6 +99,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
},
},
{
__typename: 'ViewField',
id: '2',
fieldMetadataId: '2',
position: 2,
@ -111,6 +119,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
},
},
{
__typename: 'ViewField',
id: '3',
fieldMetadataId: '3',
position: 3,
@ -209,13 +218,16 @@ describe('mapColumnDefinitionsToViewFields', () => {
const expectedViewFields = [
{
__typename: 'ViewField',
id: 'custom-id-1',
fieldMetadataId: 1,
position: 1,
isVisible: true,
definition: columnDefinitions[0],
size: undefined,
},
{
__typename: 'ViewField',
id: '',
fieldMetadataId: 2,
position: 2,

View File

@ -0,0 +1,34 @@
import { ViewFilter } from '@/views/types/ViewFilter';
export const combinedViewFilters = (
viewFilter: ViewFilter[],
toUpsertViewFilters: ViewFilter[],
toDeleteViewFilterIds: string[],
): ViewFilter[] => {
const toCreateViewFilters = toUpsertViewFilters.filter(
(toUpsertViewFilter) =>
!viewFilter.some((viewFilter) => viewFilter.id === toUpsertViewFilter.id),
);
const toUpdateViewFilters = toUpsertViewFilters.filter((toUpsertViewFilter) =>
viewFilter.some((viewFilter) => viewFilter.id === toUpsertViewFilter.id),
);
const combinedViewFilters = viewFilter
.filter((viewFilter) => !toDeleteViewFilterIds.includes(viewFilter.id))
.map((viewFilter) => {
const toUpdateViewFilter = toUpdateViewFilters.find(
(toUpdateViewFilter) => toUpdateViewFilter.id === viewFilter.id,
);
return toUpdateViewFilter ?? viewFilter;
})
.concat(toCreateViewFilters);
return Object.values(
combinedViewFilters.reduce(
(acc, obj) => ({ ...acc, [obj.fieldMetadataId]: obj }),
{},
),
);
};

View File

@ -0,0 +1,34 @@
import { ViewSort } from '@/views/types/ViewSort';
export const combinedViewSorts = (
viewSort: ViewSort[],
toUpsertViewSorts: ViewSort[],
toDeleteViewSortIds: string[],
): ViewSort[] => {
const toCreateViewSorts = toUpsertViewSorts.filter(
(toUpsertViewSort) =>
!viewSort.some((viewSort) => viewSort.id === toUpsertViewSort.id),
);
const toUpdateViewSorts = toUpsertViewSorts.filter((toUpsertViewSort) =>
viewSort.some((viewSort) => viewSort.id === toUpsertViewSort.id),
);
const combinedViewSorts = viewSort
.filter((viewSort) => !toDeleteViewSortIds.includes(viewSort.id))
.map((viewSort) => {
const toUpdateViewSort = toUpdateViewSorts.find(
(toUpdateViewSort) => toUpdateViewSort.id === viewSort.id,
);
return toUpdateViewSort ?? viewSort;
})
.concat(toCreateViewSorts);
return Object.values(
combinedViewSorts.reduce(
(acc, obj) => ({ ...acc, [obj.fieldMetadataId]: obj }),
{},
),
);
};

View File

@ -1,115 +0,0 @@
import { Snapshot } from 'recoil';
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
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,
getScopedStateDeprecated(currentViewIdScopedState, viewScopeId),
);
const familyItemId = viewId ?? currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
isPersistingViewState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
onViewTypeChangeState,
onViewCompactModeChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectMetadataIdState,
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),
isPersistingView: getSnapshotValue(snapshot, isPersistingViewState),
onViewFieldsChange: getSnapshotValue(snapshot, onViewFieldsChangeState),
onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState),
onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState),
onViewTypeChange: getSnapshotValue(snapshot, onViewTypeChangeState),
onViewCompactModeChange: getSnapshotValue(
snapshot,
onViewCompactModeChangeState,
),
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),
viewObjectMetadataId: getSnapshotValue(snapshot, viewObjectMetadataIdState),
viewType: getSnapshotValue(snapshot, viewTypeState),
views: getSnapshotValue(snapshot, viewsState),
};
};

View File

@ -1,87 +0,0 @@
import { Snapshot } from 'recoil';
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
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,
getScopedStateDeprecated(currentViewIdScopedState, viewScopeId),
);
const usedViewId = viewId ?? currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
isPersistingViewState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectMetadataIdState,
viewTypeState,
viewsState,
} = getViewScopedStates({
viewScopeId,
viewId: usedViewId,
});
return {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersReadOnlyState: canPersistFiltersSelector,
canPersistSortsReadOnlyState: canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
isPersistingViewState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeyReadOnlyState: savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeyReadOnlyState: savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeyReadOnlyState: savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectMetadataIdState,
viewTypeState,
viewsState,
};
};

View File

@ -1,225 +0,0 @@
import { getScopedFamilyStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedFamilyStateDeprecated';
import { getScopedSelectorDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedSelectorDeprecated';
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { isPersistingViewScopedState } from '@/views/states/isPersistingViewScopedState';
import { onViewCompactModeChangeScopeState } from '@/views/states/onViewCompactModeChangeScopeState';
import { onViewTypeChangeScopedState } from '@/views/states/onViewTypeChangeScopedState';
import { currentViewComponentSelector } from '@/views/states/selectors/currentViewComponentSelector';
import { savedViewFieldByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFieldByKeyScopedFamilySelector';
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 { savedViewFiltersByKeyScopedFamilySelector } from '../../states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import { savedViewSortsByKeyScopedFamilySelector } from '../../states/selectors/savedViewSortsByKeyScopedFamilySelector';
import { viewEditModeScopedState } from '../../states/viewEditModeScopedState';
import { viewObjectMetadataIdScopeState } from '../../states/viewObjectMetadataIdScopeState';
import { viewsScopedState } from '../../states/viewsScopedState';
import { viewTypeScopedState } from '../../states/viewTypeScopedState';
export const getViewScopedStates = ({
viewScopeId,
viewId,
}: {
viewScopeId: string;
viewId: string;
}) => {
const viewEditModeState = getScopedStateDeprecated(
viewEditModeScopedState,
viewScopeId,
);
const viewsState = getScopedStateDeprecated(viewsScopedState, viewScopeId);
const viewObjectMetadataIdState = getScopedStateDeprecated(
viewObjectMetadataIdScopeState,
viewScopeId,
);
const viewTypeState = getScopedStateDeprecated(
viewTypeScopedState,
viewScopeId,
);
const entityCountInCurrentViewState = getScopedStateDeprecated(
entityCountInCurrentViewScopedState,
viewScopeId,
);
const isViewBarExpandedState = getScopedStateDeprecated(
isViewBarExpandedScopedState,
viewScopeId,
);
const isPersistingViewState = getScopedStateDeprecated(
isPersistingViewScopedState,
viewScopeId,
);
// ViewSorts
const currentViewSortsState = getScopedFamilyStateDeprecated(
currentViewSortsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewSortsState = getScopedFamilyStateDeprecated(
savedViewSortsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewSortsByKeySelector = savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId,
});
const availableSortDefinitionsState = getScopedStateDeprecated(
availableSortDefinitionsScopedState,
viewScopeId,
);
const canPersistSortsSelector = canPersistViewSortsScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: viewId,
});
// ViewFilters
const currentViewFiltersState = getScopedFamilyStateDeprecated(
currentViewFiltersScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFiltersState = getScopedFamilyStateDeprecated(
savedViewFiltersScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFiltersByKeySelector =
savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: viewId,
});
const availableFilterDefinitionsState = getScopedStateDeprecated(
availableFilterDefinitionsScopedState,
viewScopeId,
);
const canPersistFiltersSelector = canPersistViewFiltersScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: viewId,
});
// ViewFields
const availableFieldDefinitionsState = getScopedStateDeprecated(
availableFieldDefinitionsScopedState,
viewScopeId,
);
const currentViewFieldsState = getScopedFamilyStateDeprecated(
currentViewFieldsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFieldsState = getScopedFamilyStateDeprecated(
savedViewFieldsScopedFamilyState,
viewScopeId,
viewId,
);
const savedViewFieldsByKeySelector = savedViewFieldByKeyScopedFamilySelector({
viewScopeId,
viewId,
});
// ViewChangeHandlers
const onViewSortsChangeState = getScopedStateDeprecated(
onViewSortsChangeScopedState,
viewScopeId,
);
const onViewFiltersChangeState = getScopedStateDeprecated(
onViewFiltersChangeScopedState,
viewScopeId,
);
const onViewFieldsChangeState = getScopedStateDeprecated(
onViewFieldsChangeScopedState,
viewScopeId,
);
const onViewTypeChangeState = getScopedStateDeprecated(
onViewTypeChangeScopedState,
viewScopeId,
);
const onViewCompactModeChangeState = getScopedStateDeprecated(
onViewCompactModeChangeScopeState,
viewScopeId,
);
const currentViewIdState = getScopedStateDeprecated(
currentViewIdScopedState,
viewScopeId,
);
const currentViewSelector = getScopedSelectorDeprecated(
currentViewComponentSelector,
viewScopeId,
);
return {
currentViewIdState,
currentViewSelector,
isViewBarExpandedState,
isPersistingViewState,
viewsState,
viewEditModeState,
viewObjectMetadataIdState,
viewTypeState,
entityCountInCurrentViewState,
availableSortDefinitionsState,
currentViewSortsState,
savedViewSortsState,
savedViewSortsByKeySelector,
canPersistSortsSelector,
availableFilterDefinitionsState,
currentViewFiltersState,
savedViewFiltersState,
savedViewFiltersByKeySelector,
canPersistFiltersSelector,
availableFieldDefinitionsState,
currentViewFieldsState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
onViewSortsChangeState,
onViewFiltersChangeState,
onViewFieldsChangeState,
onViewTypeChangeState,
onViewCompactModeChangeState,
};
};

View File

@ -1,5 +1,3 @@
import { v4 } from 'uuid';
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { ViewField } from '@/views/types/ViewField';
@ -9,7 +7,8 @@ export const mapBoardFieldDefinitionsToViewFields = (
): ViewField[] => {
return fieldsDefinitions.map(
(fieldDefinition): ViewField => ({
id: fieldDefinition.viewFieldId || v4(),
__typename: 'ViewField',
id: fieldDefinition.viewFieldId || '',
fieldMetadataId: fieldDefinition.fieldMetadataId,
size: 0,
position: fieldDefinition.position,

View File

@ -7,6 +7,7 @@ export const mapColumnDefinitionsToViewFields = (
columnDefinitions: ColumnDefinition<FieldMetadata>[],
): ViewField[] => {
return columnDefinitions.map((columnDefinition) => ({
__typename: 'ViewField',
id: columnDefinition.viewFieldId || '',
fieldMetadataId: columnDefinition.fieldMetadataId,
position: columnDefinition.position,

View File

@ -1,17 +1,29 @@
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
import { isDefined } from '~/utils/isDefined';
import { ViewFilter } from '../types/ViewFilter';
export const mapViewFiltersToFilters = (
viewFilters: ViewFilter[],
availableFilterDefinitions: FilterDefinition[],
): Filter[] => {
return viewFilters.map((viewFilter) => {
return {
fieldMetadataId: viewFilter.fieldMetadataId,
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
definition: viewFilter.definition,
};
});
return viewFilters
.map((viewFilter) => {
const availableFilterDefinition = availableFilterDefinitions.find(
(filterDefinition) =>
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
);
if (!availableFilterDefinition) return null;
return {
fieldMetadataId: viewFilter.fieldMetadataId,
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
definition: availableFilterDefinition,
};
})
.filter(isDefined);
};

View File

@ -1,13 +1,26 @@
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
import { isDefined } from '~/utils/isDefined';
import { ViewSort } from '../types/ViewSort';
export const mapViewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => {
return viewSorts.map((viewSort) => {
return {
fieldMetadataId: viewSort.fieldMetadataId,
direction: viewSort.direction,
definition: viewSort.definition,
};
});
export const mapViewSortsToSorts = (
viewSorts: ViewSort[],
availableSortDefinitions: SortDefinition[],
): Sort[] => {
return viewSorts
.map((viewSort) => {
const availableSortDefinition = availableSortDefinitions.find(
(sortDefinition) =>
sortDefinition.fieldMetadataId === viewSort.fieldMetadataId,
);
if (!availableSortDefinition) return null;
return {
fieldMetadataId: viewSort.fieldMetadataId,
direction: viewSort.direction,
definition: availableSortDefinition,
};
})
.filter(isDefined);
};