Simplify last visited (#10259)

In this PR, I'm simplifying the lastVisitedView / Object logic:
- removing fallback logic as it's not useful
- splitting hooks into smaller hooks (to avoir re-renders)
- removing componentState on those states that are global
This commit is contained in:
Charles Bochet
2025-02-17 17:27:28 +01:00
committed by GitHub
parent a526472ddc
commit 5b4cb4bd2c
22 changed files with 249 additions and 278 deletions

View File

@ -48,6 +48,7 @@ export const MainContextStoreProvider = () => {
const [searchParams] = useSearchParams();
const viewIdQueryParam = searchParams.get('viewId');
// Todo: this is triggering a lot of re-renders as we update the viewFields, we should introduce a state here
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);

View File

@ -1,8 +1,8 @@
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
import { useSetLastVisitedObjectMetadataId } from '@/navigation/hooks/useSetLastVisitedObjectMetadataId';
import { useSetLastVisitedViewForObjectMetadataNamePlural } from '@/navigation/hooks/useSetLastVisitedViewForObjectMetadataNamePlural';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useEffect } from 'react';
@ -22,15 +22,11 @@ export const MainContextStoreProviderEffect = ({
setMainContextStoreComponentInstanceId,
] = useRecoilState(mainContextStoreComponentInstanceIdState);
const { getLastVisitedViewIdFromObjectNamePlural, setLastVisitedView } =
useLastVisitedView();
const { setLastVisitedViewForObjectMetadataNamePlural } =
useSetLastVisitedViewForObjectMetadataNamePlural();
const { lastVisitedObjectMetadataItemId, setLastVisitedObjectMetadataItem } =
useLastVisitedObjectMetadataItem();
const lastVisitedViewId = getLastVisitedViewIdFromObjectNamePlural(
objectMetadataItem.namePlural,
);
const { setLastVisitedObjectMetadataId } =
useSetLastVisitedObjectMetadataId();
const [contextStoreCurrentViewId, setContextStoreCurrentViewId] =
useRecoilComponentStateV2(
@ -60,16 +56,14 @@ export const MainContextStoreProviderEffect = ({
);
}
if (viewId !== lastVisitedViewId) {
setLastVisitedView({
objectNamePlural: objectMetadataItem.namePlural,
viewId: viewId,
});
}
setLastVisitedViewForObjectMetadataNamePlural({
objectNamePlural: objectMetadataItem.namePlural,
viewId: viewId,
});
if (objectMetadataItem.id !== lastVisitedObjectMetadataItemId) {
setLastVisitedObjectMetadataItem(objectMetadataItem.namePlural);
}
setLastVisitedObjectMetadataId({
objectMetadataItemId: objectMetadataItem.id,
});
if (contextStoreCurrentViewId !== viewId) {
setContextStoreCurrentViewId(viewId);
@ -77,16 +71,14 @@ export const MainContextStoreProviderEffect = ({
}, [
contextStoreCurrentObjectMetadataId,
contextStoreCurrentViewId,
lastVisitedObjectMetadataItemId,
lastVisitedViewId,
mainContextStoreComponentInstanceId,
mainContextStoreComponentInstanceIdToSet,
objectMetadataItem,
objectMetadataItem.namePlural,
setContextStoreCurrentObjectMetadataId,
setContextStoreCurrentViewId,
setLastVisitedObjectMetadataItem,
setLastVisitedView,
setLastVisitedObjectMetadataId,
setLastVisitedViewForObjectMetadataNamePlural,
setMainContextStoreComponentInstanceId,
viewId,
]);

View File

@ -1,5 +1,5 @@
import { currentUserState } from '@/auth/states/currentUserState';
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
import { ObjectPathInfo } from '@/navigation/types/ObjectPathInfo';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
@ -16,8 +16,9 @@ export const useDefaultHomePagePath = () => {
const { activeObjectMetadataItems, alphaSortedActiveObjectMetadataItems } =
useFilteredObjectMetadataItems();
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const { lastVisitedObjectMetadataItemId } =
useLastVisitedObjectMetadataItem();
const lastVisitedObjectMetadataItemId = useRecoilValue(
lastVisitedObjectMetadataItemIdState,
);
const getActiveObjectMetadataItemMatchingId = useCallback(
(objectMetadataId: string) => {

View File

@ -1,70 +0,0 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { lastVisitedObjectMetadataItemIdStateSelector } from '@/navigation/states/selectors/lastVisitedObjectMetadataItemIdStateSelector';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { AppPath } from '@/types/AppPath';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { getAppPath } from '~/utils/navigation/getAppPath';
export const useLastVisitedObjectMetadataItem = () => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const scopeId = currentWorkspace?.id ?? '';
const lastVisitedObjectMetadataItemIdState = extractComponentState(
lastVisitedObjectMetadataItemIdStateSelector,
scopeId,
);
const [lastVisitedObjectMetadataItemId, setLastVisitedObjectMetadataItemId] =
useRecoilState(lastVisitedObjectMetadataItemIdState);
const {
findActiveObjectMetadataItemByNamePlural,
alphaSortedActiveObjectMetadataItems,
} = useFilteredObjectMetadataItems();
const setNavigationMemorizedUrl = useSetRecoilState(
navigationMemorizedUrlState,
);
const setFallbackForLastVisitedObjectMetadataItem = (
objectMetadataItemId: string,
) => {
const isDeactivateDefault = isDeeplyEqual(
lastVisitedObjectMetadataItemId,
objectMetadataItemId,
);
const [newFallbackObjectMetadataItem] =
alphaSortedActiveObjectMetadataItems.filter(
(item) => item.id !== objectMetadataItemId,
);
if (isDeactivateDefault) {
setLastVisitedObjectMetadataItemId(newFallbackObjectMetadataItem.id);
setNavigationMemorizedUrl(
getAppPath(AppPath.RecordIndexPage, {
objectNamePlural: newFallbackObjectMetadataItem.namePlural,
}),
);
}
};
const setLastVisitedObjectMetadataItem = (objectNamePlural: string) => {
const fallbackObjectMetadataItem =
findActiveObjectMetadataItemByNamePlural(objectNamePlural);
if (isDefined(fallbackObjectMetadataItem)) {
setLastVisitedObjectMetadataItemId(fallbackObjectMetadataItem.id);
}
};
return {
lastVisitedObjectMetadataItemId,
setLastVisitedObjectMetadataItem,
setFallbackForLastVisitedObjectMetadataItem,
};
};

View File

@ -1,54 +1,15 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { lastVisitedViewPerObjectMetadataItemStateSelector } from '@/navigation/states/selectors/lastVisitedViewPerObjectMetadataItemStateSelector';
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import { useRecoilValue } from 'recoil';
export const useLastVisitedView = () => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const scopeId = currentWorkspace?.id ?? '';
const lastVisitedViewPerObjectMetadataItemState = extractComponentState(
lastVisitedViewPerObjectMetadataItemStateSelector,
scopeId,
const lastVisitedViewPerObjectMetadataItem = useRecoilValue(
lastVisitedViewPerObjectMetadataItemState,
);
const [
lastVisitedViewPerObjectMetadataItem,
setLastVisitedViewPerObjectMetadataItem,
] = useRecoilState(lastVisitedViewPerObjectMetadataItemState);
const { findActiveObjectMetadataItemByNamePlural } =
useFilteredObjectMetadataItems();
const setFallbackForLastVisitedView = (objectMetadataItemId: string) => {
/* ...{} allows us to pass value as undefined to remove that particular key
even though param type is of type Record<string,string> */
setLastVisitedViewPerObjectMetadataItem({
...{},
[objectMetadataItemId]: undefined,
});
};
const setLastVisitedView = ({
objectNamePlural,
viewId,
}: {
objectNamePlural: string;
viewId: string;
}) => {
const objectMetadataItem =
findActiveObjectMetadataItemByNamePlural(objectNamePlural);
if (isDefined(objectMetadataItem)) {
setLastVisitedViewPerObjectMetadataItem({
[objectMetadataItem.id]: viewId,
});
}
};
const getLastVisitedViewIdFromObjectNamePlural = (
objectNamePlural: string,
) => {
@ -65,9 +26,7 @@ export const useLastVisitedView = () => {
return lastVisitedViewPerObjectMetadataItem?.[objectMetadataItemId];
};
return {
setLastVisitedView,
getLastVisitedViewIdFromObjectNamePlural,
getLastVisitedViewIdFromObjectMetadataItemId,
setFallbackForLastVisitedView,
};
};

View File

@ -1,12 +1,12 @@
import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { useRecoilState } from 'recoil';
import { useSetRecoilState } from 'recoil';
export const useOpenSettingsMenu = () => {
const [, setIsNavigationDrawerExpanded] = useRecoilState(
const setIsNavigationDrawerExpanded = useSetRecoilState(
isNavigationDrawerExpandedState,
);
const [, setCurrentMobileNavigationDrawer] = useRecoilState(
const setCurrentMobileNavigationDrawer = useSetRecoilState(
currentMobileNavigationDrawerState,
);

View File

@ -0,0 +1,35 @@
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared';
export const useSetLastVisitedObjectMetadataId = () => {
const setLastVisitedObjectMetadataId = useRecoilCallback(
({ set, snapshot }) =>
({ objectMetadataItemId }: { objectMetadataItemId: string }) => {
const objectMetadataItems = snapshot
.getLoadable(objectMetadataItemsState)
.getValue();
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === objectMetadataItemId,
);
const lastVisitedObjectMetadataItemId = snapshot
.getLoadable(lastVisitedObjectMetadataItemIdState)
.getValue();
if (
isDefined(objectMetadataItem) &&
lastVisitedObjectMetadataItemId !== objectMetadataItemId
) {
set(lastVisitedObjectMetadataItemIdState, objectMetadataItemId);
}
},
[],
);
return {
setLastVisitedObjectMetadataId,
};
};

View File

@ -0,0 +1,63 @@
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useLazyPrefetchedData } from '@/prefetch/hooks/useLazyPrefetchData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared';
export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
const { records: views, findManyRecords } = useLazyPrefetchedData<View>(
PrefetchKey.AllViews,
);
const setLastVisitedViewForObjectMetadataNamePlural = useRecoilCallback(
({ set, snapshot }) =>
async ({
objectNamePlural,
viewId,
}: {
objectNamePlural: string;
viewId: string;
}) => {
await findManyRecords();
const view = views.find((view: View) => view.id === viewId);
const objectMetadataItems = snapshot
.getLoadable(objectMetadataItemsState)
.getValue();
const objectMetadataItem = objectMetadataItems.find(
(item) => item.namePlural === objectNamePlural,
);
if (!isDefined(objectMetadataItem) || !isDefined(view)) {
return;
}
if (view.objectMetadataId !== objectMetadataItem.id) {
return;
}
const lastVisitedViewPerObjectMetadataItem = snapshot
.getLoadable(lastVisitedViewPerObjectMetadataItemState)
.getValue();
const lastVisitedViewId =
lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem?.id];
if (isDefined(objectMetadataItem) && lastVisitedViewId !== viewId) {
set(lastVisitedViewPerObjectMetadataItemState, {
...lastVisitedViewPerObjectMetadataItem,
[objectMetadataItem.id]: viewId,
});
}
},
[findManyRecords, views],
);
return {
setLastVisitedViewForObjectMetadataNamePlural,
};
};

View File

@ -1,10 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createState } from 'twenty-ui';
import { localStorageEffect } from '~/utils/recoil-effects';
export const lastVisitedObjectMetadataItemIdState = createComponentState<Record<
string,
string
> | null>({
export const lastVisitedObjectMetadataItemIdState = createState<string | null>({
key: 'lastVisitedObjectMetadataItemIdState',
defaultValue: null,
effects: [localStorageEffect()],

View File

@ -1,9 +1,11 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createState } from 'twenty-ui';
import { localStorageEffect } from '~/utils/recoil-effects';
export const lastVisitedViewPerObjectMetadataItemState =
createComponentState<Record<string, string> | null>({
key: 'lastVisitedViewPerObjectMetadataItemState',
defaultValue: null,
effects: [localStorageEffect()],
});
export const lastVisitedViewPerObjectMetadataItemState = createState<Record<
string,
string
> | null>({
key: 'lastVisitedViewPerObjectMetadataItemState',
defaultValue: null,
effects: [localStorageEffect()],
});

View File

@ -1,24 +0,0 @@
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector';
export const lastVisitedObjectMetadataItemIdStateSelector =
createComponentSelector<string | null>({
key: 'lastVisitedObjectMetadataItemIdStateSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
const state = get(lastVisitedObjectMetadataItemIdState({ scopeId }));
return state?.['last_visited_object']
? state['last_visited_object']
: null;
},
set:
({ scopeId }: { scopeId: string }) =>
({ set }, newValue) => {
set(lastVisitedObjectMetadataItemIdState({ scopeId }), {
...(typeof newValue === 'string' && {
last_visited_object: newValue,
}),
});
},
});

View File

@ -1,34 +0,0 @@
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector';
import { isDefined } from 'twenty-shared';
export const lastVisitedViewPerObjectMetadataItemStateSelector =
createComponentSelector<Record<string, string> | null>({
key: 'lastVisitedViewPerObjectMetadataItemStateSelector',
get:
({ scopeId }: { scopeId: string }) =>
({ get }) => {
const state = get(
lastVisitedViewPerObjectMetadataItemState({ scopeId }),
);
if (isDefined(state?.['last_visited_object'])) {
const { last_visited_object: _last_visited_object, ...rest } = state;
return rest;
}
return state;
},
set:
({ scopeId }: { scopeId: string }) =>
({ set, get }, newValue) => {
const currentLastVisitedViewPerObjectMetadataItems = get(
lastVisitedViewPerObjectMetadataItemStateSelector({ scopeId }),
);
set(lastVisitedViewPerObjectMetadataItemState({ scopeId }), {
...currentLastVisitedViewPerObjectMetadataItems,
...newValue,
});
},
});

View File

@ -1,4 +1,6 @@
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
@ -7,9 +9,11 @@ import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/componen
import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer';
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { View } from '@/views/types/View';
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
import { useLocation } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { AnimatedExpandableContainer, useIcons } from 'twenty-ui';
import { getAppPath } from '~/utils/navigation/getAppPath';
@ -27,20 +31,29 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
views,
);
const { getIcon } = useIcons();
const currentPath = useLocation().pathname;
const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView();
const lastVisitedViewId = getLastVisitedViewIdFromObjectMetadataItemId(
objectMetadataItem.id,
const mainContextStoreComponentInstanceId = useRecoilValue(
mainContextStoreComponentInstanceIdState,
);
const viewId = lastVisitedViewId ?? objectMetadataViews[0]?.id;
const contextStoreCurrentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
mainContextStoreComponentInstanceId,
);
const lastVisitedViewPerObjectMetadataItem = useRecoilValue(
lastVisitedViewPerObjectMetadataItemState,
);
const lastVisitedViewId =
lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem.id];
const { getIcon } = useIcons();
const currentPath = useLocation().pathname;
const navigationPath = getAppPath(
AppPath.RecordIndexPage,
{ objectNamePlural: objectMetadataItem.namePlural },
viewId ? { viewId } : undefined,
lastVisitedViewId ? { viewId: lastVisitedViewId } : undefined,
);
const isActive =
@ -62,7 +75,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
);
const selectedSubItemIndex = sortedObjectMetadataViews.findIndex(
(view) => viewId === view.id,
(view) => contextStoreCurrentViewId === view.id,
);
const subItemArrayLength = sortedObjectMetadataViews.length;
@ -93,7 +106,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
{ objectNamePlural: objectMetadataItem.namePlural },
{ viewId: view.id },
)}
active={viewId === view.id}
active={contextStoreCurrentViewId === view.id}
subItemState={getNavigationSubItemLeftAdornment({
index,
arrayLength: subItemArrayLength,

View File

@ -0,0 +1,14 @@
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { useRecoilValue } from 'recoil';
export const useFilterableFieldMetadataItems = (
objectMetadataItemId: string,
) => {
const filterableFieldMetadataItems = useRecoilValue(
availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId,
}),
);
return { filterableFieldMetadataItems };
};

View File

@ -1,14 +1,11 @@
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecoilValue } from 'recoil';
export const useFilterableFieldMetadataItemsInRecordIndexContext = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const filterableFieldMetadataItems = useRecoilValue(
availableFieldMetadataItemsForFilterFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
objectMetadataItem.id,
);
return { filterableFieldMetadataItems };

View File

@ -26,7 +26,7 @@ import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActio
import { ContextStoreCurrentViewTypeEffect } from '@/context-store/components/ContextStoreCurrentViewTypeEffect';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup';
import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect';
import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect';
@ -180,8 +180,9 @@ export const RecordIndexContainer = () => {
contextStoreTargetedRecordsRuleComponentState,
);
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
objectMetadataItem.id,
);
const isCommandMenuV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IsCommandMenuV2Enabled,

View File

@ -0,0 +1,30 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
export const useLazyPrefetchedData = <T extends ObjectRecord>(
prefetchKey: PrefetchKey,
filter?: RecordGqlOperationFilter,
) => {
const { operationSignatureFactory, objectNameSingular } =
PREFETCH_CONFIG[prefetchKey];
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const recordGqlFields =
operationSignatureFactory({ objectMetadataItem }).fields ?? filter;
const { records, findManyRecords } = useLazyFindManyRecords<T>({
objectNameSingular: objectNameSingular,
recordGqlFields,
});
return {
findManyRecords,
records,
};
};

View File

@ -9,8 +9,6 @@ import {
useIcons,
} from 'twenty-ui';
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard';
import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/components/SettingsDataModelObjectTypeTag';
@ -44,12 +42,8 @@ export const SettingsObjectSummaryCard = ({
const theme = useTheme();
const { getIcon } = useIcons();
const Icon = getIcon(iconKey);
const objectMetadataItemId = objectMetadataItem.id;
const { closeDropdown } = useDropdown(dropdownId);
const { setFallbackForLastVisitedView } = useLastVisitedView();
const { setFallbackForLastVisitedObjectMetadataItem } =
useLastVisitedObjectMetadataItem();
const handleEdit = () => {
onEdit();
@ -57,8 +51,6 @@ export const SettingsObjectSummaryCard = ({
};
const handleDeactivate = () => {
setFallbackForLastVisitedObjectMetadataItem(objectMetadataItemId);
setFallbackForLastVisitedView(objectMetadataItemId);
onDeactivate();
closeDropdown();
};

View File

@ -3,10 +3,8 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { zodResolver } from '@hookform/resolvers/zod';
import { FormProvider, useForm } from 'react-hook-form';
import { Button, H2Title, IconArchive, Section } from 'twenty-ui';
import { z, ZodError } from 'zod';
import { ZodError, z } from 'zod';
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import {
@ -17,11 +15,9 @@ import {
import { settingsDataModelObjectIdentifiersFormSchema } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectIdentifiersForm';
import { SettingsDataModelObjectSettingsFormCard } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectSettingsFormCard';
import { settingsUpdateObjectInputSchema } from '@/settings/data-model/validation-schemas/settingsUpdateObjectInputSchema';
import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import pick from 'lodash.pick';
@ -29,7 +25,6 @@ import { useSetRecoilState } from 'recoil';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState';
import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
import { getAppPath } from '~/utils/navigation/getAppPath';
const objectEditFormSchema = z
.object({})
@ -63,9 +58,6 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => {
);
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
const { lastVisitedObjectMetadataItemId } =
useLastVisitedObjectMetadataItem();
const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView();
const formConfig = useForm<SettingsDataModelObjectEditFormValues>({
mode: 'onTouched',
@ -73,10 +65,6 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => {
});
const { isDirty } = formConfig.formState;
const setNavigationMemorizedUrl = useSetRecoilState(
navigationMemorizedUrlState,
);
const getUpdatePayload = (
formValues: SettingsDataModelObjectEditFormValues,
) => {
@ -143,19 +131,6 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => {
formConfig.reset(undefined, { keepValues: true });
if (lastVisitedObjectMetadataItemId === objectMetadataItem.id) {
const lastVisitedView = getLastVisitedViewIdFromObjectMetadataItemId(
objectMetadataItem.id,
);
setNavigationMemorizedUrl(
getAppPath(
AppPath.RecordIndexPage,
{ objectNamePlural: objectNamePluralForRedirection },
{ viewId: lastVisitedView },
),
);
}
navigate(SettingsPath.ObjectDetail, {
objectNamePlural: objectNamePluralForRedirection,
});

View File

@ -1,15 +1,18 @@
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { hasInitializedCurrentRecordFiltersComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordFiltersComponentFamilyState';
import { View } from '@/views/types/View';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
export const ViewBarRecordFilterEffect = () => {
@ -17,7 +20,20 @@ export const ViewBarRecordFilterEffect = () => {
PrefetchKey.AllViews,
);
const currentViewId = useRecoilComponentValueV2(currentViewIdComponentState);
const currentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
);
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id === contextStoreCurrentObjectMetadataId,
);
const [
hasInitializedCurrentRecordFilters,
@ -25,7 +41,7 @@ export const ViewBarRecordFilterEffect = () => {
] = useRecoilComponentFamilyStateV2(
hasInitializedCurrentRecordFiltersComponentFamilyState,
{
viewId: currentViewId,
viewId: currentViewId ?? undefined,
},
);
@ -37,13 +53,18 @@ export const ViewBarRecordFilterEffect = () => {
currentRecordFiltersComponentState,
);
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
objectMetadataItem?.id,
);
useEffect(() => {
if (isDataPrefetched && !hasInitializedCurrentRecordFilters) {
const currentView = views.find((view) => view.id === currentViewId);
if (currentView?.objectMetadataId !== objectMetadataItem?.id) {
return;
}
if (isDefined(currentView)) {
setCurrentRecordFilters(
mapViewFiltersToFilters(
@ -63,6 +84,7 @@ export const ViewBarRecordFilterEffect = () => {
currentRecordFilters,
hasInitializedCurrentRecordFilters,
setHasInitializedCurrentRecordFilters,
objectMetadataItem?.id,
]);
return null;

View File

@ -1,5 +1,6 @@
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilter } from '@/views/types/ViewFilter';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
@ -9,8 +10,11 @@ export const useApplyViewFiltersToCurrentRecordFilters = () => {
currentRecordFiltersComponentState,
);
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
objectMetadataItem.id,
);
const applyViewFiltersToCurrentRecordFilters = (
viewFilters: ViewFilter[],

View File

@ -43,7 +43,8 @@ export const RecordIndexPage = () => {
mainContextStoreComponentInstanceId,
);
const recordIndexId = `${objectNamePlural}-${contextStoreCurrentViewId}`;
// Todo: if we want the recordIndexId to contain the viewId, we need to remove the Effects otherwise we will have race condition on view change
const recordIndexId = `${objectNamePlural}`;
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural,