View module refactor with atomic recoil component instance states (#6810)

This PR refactors the view module to implement utils that avoid having
to create hooks to inject the scope id in the states, like
`useViewStates`, each componentState will know its unique related
InstanceContext (which holds the instanceId), and thus will be able to
retrieve it itself.

We keep the naming componentState as it reflects the fact that those
states are tied to instances of a component (or its children).

We introduce the instance word where it is needed, in place of scopeId
for example, to precise the fact that we handle instances of component
state, one for each instance of a component.

For example, the currentViewId is a state that is tied to an instance of
the ViewBar, but as we can switch between views, we want currentViewId
to be a componentState tied to an instance of the ViewBar component.

This PR also refactors view filter and sort states to fix this issue :
https://github.com/twentyhq/twenty/issues/6837 and other problems
involving resetting those states between page navigation.

Fixes https://github.com/twentyhq/twenty/issues/6837

---------

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

View File

@ -1,10 +1,12 @@
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const availableFieldDefinitionsComponentState = createComponentState<
export const availableFieldDefinitionsComponentState = createComponentStateV2<
ColumnDefinition<FieldMetadata>[]
>({
key: 'availableFieldDefinitionsComponentState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,9 +1,11 @@
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const availableFilterDefinitionsComponentState = createComponentState<
export const availableFilterDefinitionsComponentState = createComponentStateV2<
FilterDefinition[]
>({
key: 'availableFilterDefinitionsComponentState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,9 +1,11 @@
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const availableSortDefinitionsComponentState = createComponentState<
export const availableSortDefinitionsComponentState = createComponentStateV2<
SortDefinition[]
>({
key: 'availableSortDefinitionsComponentState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -0,0 +1,3 @@
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
export const ViewComponentInstanceContext = createComponentInstanceContext();

View File

@ -1,8 +1,10 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const currentViewIdComponentState = createComponentState<
export const currentViewIdComponentState = createComponentStateV2<
string | undefined
>({
key: 'currentViewIdComponentState',
defaultValue: undefined,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,8 +1,10 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const entityCountInCurrentViewComponentState = createComponentState<
export const entityCountInCurrentViewComponentState = createComponentStateV2<
number | undefined
>({
key: 'entityCountInCurrentViewComponentState',
defaultValue: undefined,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,7 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const isCurrentViewKeyIndexComponentState =
createComponentState<boolean>({
createComponentStateV2<boolean>({
key: 'isCurrentViewKeyIndexComponentState',
defaultValue: true,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,7 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const isPersistingViewFieldsComponentState =
createComponentState<boolean>({
createComponentStateV2<boolean>({
key: 'isPersistingViewFieldsComponentState',
defaultValue: false,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,6 +1,8 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const isViewBarExpandedComponentState = createComponentState<boolean>({
export const isViewBarExpandedComponentState = createComponentStateV2<boolean>({
key: 'isViewBarExpandedComponentState',
defaultValue: true,
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,9 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { View } from '@/views/types/View';
export const onCurrentViewChangeComponentState = createComponentState<
((view: View | undefined) => void | Promise<void>) | undefined
>({
key: 'onCurrentViewChangeComponentState',
defaultValue: undefined,
});

View File

@ -0,0 +1,42 @@
import { createComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { unsavedToDeleteViewFilterIdsComponentFamilyState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentFamilyState';
import { unsavedToDeleteViewSortIdsComponentFamilyState } from '@/views/states/unsavedToDeleteViewSortIdsComponentFamilyState';
import { unsavedToUpsertViewFiltersComponentFamilyState } from '@/views/states/unsavedToUpsertViewFiltersComponentFamilyState';
import { unsavedToUpsertViewSortsComponentFamilyState } from '@/views/states/unsavedToUpsertViewSortsComponentFamilyState';
export const canPersistViewComponentFamilySelector =
createComponentFamilySelectorV2<boolean, { viewId?: string }>({
key: 'canPersistViewComponentFamilySelector',
get:
({ familyKey, instanceId }) =>
({ get }) => {
return (
get(
unsavedToUpsertViewFiltersComponentFamilyState.atomFamily({
familyKey,
instanceId,
}),
).length > 0 ||
get(
unsavedToUpsertViewSortsComponentFamilyState.atomFamily({
familyKey,
instanceId,
}),
).length > 0 ||
get(
unsavedToDeleteViewFilterIdsComponentFamilyState.atomFamily({
familyKey,
instanceId,
}),
).length > 0 ||
get(
unsavedToDeleteViewSortIdsComponentFamilyState.atomFamily({
familyKey,
instanceId,
}),
).length > 0
);
},
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,21 +0,0 @@
import { selectorFamily } from 'recoil';
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
export const canPersistViewComponentSelector = selectorFamily({
key: 'canPersistViewComponentSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
return (
get(unsavedToUpsertViewFiltersComponentState({ scopeId })).length > 0 ||
get(unsavedToUpsertViewSortsComponentState({ scopeId })).length > 0 ||
get(unsavedToDeleteViewFilterIdsComponentState({ scopeId })).length >
0 ||
get(unsavedToDeleteViewSortIdsComponentState({ scopeId })).length > 0
);
},
});

View File

@ -1,22 +0,0 @@
import { selectorFamily } from 'recoil';
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
export const canResetViewComponentSelector = selectorFamily({
key: 'canResetViewComponentSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
return (
get(unsavedToUpsertViewFiltersComponentState({ scopeId })).length ===
0 &&
get(unsavedToUpsertViewSortsComponentState({ scopeId })).length === 0 &&
get(unsavedToDeleteViewFilterIdsComponentState({ scopeId })).length ===
0 &&
get(unsavedToDeleteViewSortIdsComponentState({ scopeId })).length === 0
);
},
});

View File

@ -0,0 +1,9 @@
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const unsavedToDeleteViewFilterIdsComponentFamilyState =
createComponentFamilyStateV2<string[], { viewId?: string }>({
key: 'unsavedToDeleteViewFilterIdsComponentFamilyState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,8 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const unsavedToDeleteViewFilterIdsComponentState = createComponentState<
string[]
>({
key: 'unsavedToDeleteViewFilterIdsComponentState',
defaultValue: [],
});

View File

@ -0,0 +1,9 @@
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const unsavedToDeleteViewSortIdsComponentFamilyState =
createComponentFamilyStateV2<string[], { viewId?: string }>({
key: 'unsavedToDeleteViewSortIdsComponentFamilyState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,8 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const unsavedToDeleteViewSortIdsComponentState = createComponentState<
string[]
>({
key: 'unsavedToDeleteViewSortIdsComponentState',
defaultValue: [],
});

View File

@ -0,0 +1,10 @@
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { ViewFilter } from '../types/ViewFilter';
export const unsavedToUpsertViewFiltersComponentFamilyState =
createComponentFamilyStateV2<ViewFilter[], { viewId?: string }>({
key: 'unsavedToUpsertViewFiltersComponentFamilyState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,10 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { ViewFilter } from '../types/ViewFilter';
export const unsavedToUpsertViewFiltersComponentState = createComponentState<
ViewFilter[]
>({
key: 'unsavedToUpsertViewFiltersComponentState',
defaultValue: [],
});

View File

@ -0,0 +1,10 @@
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { ViewSort } from '../types/ViewSort';
export const unsavedToUpsertViewSortsComponentFamilyState =
createComponentFamilyStateV2<ViewSort[], { viewId?: string }>({
key: 'unsavedToUpsertViewSortsComponentFamilyState',
defaultValue: [],
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -1,10 +0,0 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { ViewSort } from '../types/ViewSort';
export const unsavedToUpsertViewSortsComponentState = createComponentState<
ViewSort[]
>({
key: 'unsavedToUpsertViewSortsComponentState',
defaultValue: [],
});

View File

@ -1,8 +1,10 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const viewObjectMetadataIdComponentState = createComponentState<
export const viewObjectMetadataIdComponentState = createComponentStateV2<
string | undefined
>({
key: 'viewObjectMetadataIdComponentState',
defaultValue: undefined,
componentInstanceContext: ViewComponentInstanceContext,
});