diff --git a/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts b/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts index 04374b388..15574875c 100644 --- a/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts +++ b/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts @@ -6,6 +6,7 @@ import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceSta import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; +import { arePrefetchViewsLoadedState } from '@/prefetch/states/arePrefetchViewsLoaded'; import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState'; import { AppPath } from '@/types/AppPath'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -31,8 +32,12 @@ const renderHooks = ({ objectMetadataItemsState, ); const setPrefetchViews = useSetRecoilState(prefetchViewsState); + const setArePrefetchViewsLoaded = useSetRecoilState( + arePrefetchViewsLoadedState, + ); setObjectMetadataItems(generatedMockObjectMetadataItems); + setArePrefetchViewsLoaded(true); if (withExistingView) { setPrefetchViews([ @@ -56,6 +61,8 @@ const renderHooks = ({ __typename: 'View', }, ]); + } else { + setPrefetchViews([]); } if (withCurrentUser) { @@ -70,6 +77,7 @@ const renderHooks = ({ ); return { result }; }; + describe('useDefaultHomePagePath', () => { it('should return proper path when no currentUser', () => { const { result } = renderHooks({ diff --git a/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts b/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts index a8a965f02..310b860fa 100644 --- a/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts +++ b/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts @@ -3,12 +3,13 @@ import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVi import { ObjectPathInfo } from '@/navigation/types/ObjectPathInfo'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; +import { arePrefetchViewsLoadedState } from '@/prefetch/states/arePrefetchViewsLoaded'; import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import isEmpty from 'lodash.isempty'; import { useCallback, useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useRecoilCallback, useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { getAppPath } from '~/utils/navigation/getAppPath'; @@ -21,6 +22,8 @@ export const useDefaultHomePagePath = () => { alphaSortedActiveNonSystemObjectMetadataItems, } = useFilteredObjectMetadataItems(); + const arePrefetchViewsLoaded = useRecoilValue(arePrefetchViewsLoadedState); + const readableAlphaSortedActiveNonSystemObjectMetadataItems = useMemo(() => { return alphaSortedActiveNonSystemObjectMetadataItems.filter((item) => { const objectPermissions = objectPermissionsByObjectMetadataId[item.id]; @@ -31,11 +34,6 @@ export const useDefaultHomePagePath = () => { objectPermissionsByObjectMetadataId, ]); - const prefetchViews = useRecoilValue(prefetchViewsState); - const lastVisitedObjectMetadataItemId = useRecoilValue( - lastVisitedObjectMetadataItemIdState, - ); - const getActiveObjectMetadataItemMatchingId = useCallback( (objectMetadataId: string) => { return activeNonSystemObjectMetadataItems.find( @@ -45,12 +43,21 @@ export const useDefaultHomePagePath = () => { [activeNonSystemObjectMetadataItems], ); - const getFirstView = useCallback( - (objectMetadataItemId: string | undefined | null) => - prefetchViews.find( - (view) => view.objectMetadataId === objectMetadataItemId, - ), - [prefetchViews], + const getFirstView = useRecoilCallback( + ({ snapshot }) => { + return (objectMetadataItemId: string | undefined | null) => { + if (!arePrefetchViewsLoaded) { + return undefined; + } + + const views = snapshot.getLoadable(prefetchViewsState).getValue(); + + return views.find( + (view) => view.objectMetadataId === objectMetadataItemId, + ); + }; + }, + [arePrefetchViewsLoaded], ); const firstObjectPathInfo = useMemo(() => { @@ -64,38 +71,51 @@ export const useDefaultHomePagePath = () => { const view = getFirstView(firstObjectMetadataItem?.id); return { objectMetadataItem: firstObjectMetadataItem, view }; - }, [readableAlphaSortedActiveNonSystemObjectMetadataItems, getFirstView]); + }, [getFirstView, readableAlphaSortedActiveNonSystemObjectMetadataItems]); - const defaultObjectPathInfo = useMemo(() => { - if ( - !isDefined(lastVisitedObjectMetadataItemId) || - !objectPermissionsByObjectMetadataId[lastVisitedObjectMetadataItemId] - ?.canReadObjectRecords - ) { - return firstObjectPathInfo; - } + const getDefaultObjectPathInfo = useRecoilCallback( + ({ snapshot }) => { + return () => { + const lastVisitedObjectMetadataItemId = snapshot + .getLoadable(lastVisitedObjectMetadataItemIdState) + .getValue(); - const lastVisitedObjectMetadataItem = getActiveObjectMetadataItemMatchingId( - lastVisitedObjectMetadataItemId, - ); + if ( + !isDefined(lastVisitedObjectMetadataItemId) || + !objectPermissionsByObjectMetadataId[lastVisitedObjectMetadataItemId] + ?.canReadObjectRecords + ) { + return firstObjectPathInfo; + } - if (isDefined(lastVisitedObjectMetadataItem)) { - return { - view: getFirstView(lastVisitedObjectMetadataItemId), - objectMetadataItem: lastVisitedObjectMetadataItem, + const lastVisitedObjectMetadataItem = + getActiveObjectMetadataItemMatchingId( + lastVisitedObjectMetadataItemId, + ); + + if (isDefined(lastVisitedObjectMetadataItem)) { + return { + view: getFirstView(lastVisitedObjectMetadataItemId), + objectMetadataItem: lastVisitedObjectMetadataItem, + }; + } + + return firstObjectPathInfo; }; - } - - return firstObjectPathInfo; - }, [ - firstObjectPathInfo, - getActiveObjectMetadataItemMatchingId, - getFirstView, - lastVisitedObjectMetadataItemId, - objectPermissionsByObjectMetadataId, - ]); + }, + [ + firstObjectPathInfo, + getActiveObjectMetadataItemMatchingId, + getFirstView, + objectPermissionsByObjectMetadataId, + ], + ); const defaultHomePagePath = useMemo(() => { + if (!arePrefetchViewsLoaded) { + return undefined; + } + if (!isDefined(currentUser)) { return AppPath.SignInUp; } @@ -104,6 +124,8 @@ export const useDefaultHomePagePath = () => { return `${AppPath.Settings}/${SettingsPath.ProfilePage}`; } + const defaultObjectPathInfo = getDefaultObjectPathInfo(); + if (!isDefined(defaultObjectPathInfo)) { return AppPath.NotFound; } @@ -117,8 +139,9 @@ export const useDefaultHomePagePath = () => { viewId ? { viewId } : undefined, ); }, [ + arePrefetchViewsLoaded, currentUser, - defaultObjectPathInfo, + getDefaultObjectPathInfo, readableAlphaSortedActiveNonSystemObjectMetadataItems, ]); diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunViewQueryEffect.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunViewQueryEffect.tsx index da8e9485a..bff1ac0f7 100644 --- a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunViewQueryEffect.tsx +++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunViewQueryEffect.tsx @@ -6,6 +6,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { findAllViewsOperationSignatureFactory } from '@/prefetch/graphql/operation-signatures/factories/findAllViewsOperationSignatureFactory'; +import { arePrefetchViewsLoadedState } from '@/prefetch/states/arePrefetchViewsLoaded'; import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState'; import { isPersistingViewFieldsState } from '@/views/states/isPersistingViewFieldsState'; import { View } from '@/views/types/View'; @@ -45,6 +46,7 @@ export const PrefetchRunViewQueryEffect = () => { if (!isDeeplyEqual(existingViews, views)) { set(prefetchViewsState, views); + set(arePrefetchViewsLoadedState, true); } }, [], diff --git a/packages/twenty-front/src/modules/prefetch/states/arePrefetchViewsLoaded.ts b/packages/twenty-front/src/modules/prefetch/states/arePrefetchViewsLoaded.ts new file mode 100644 index 000000000..2b0a4a599 --- /dev/null +++ b/packages/twenty-front/src/modules/prefetch/states/arePrefetchViewsLoaded.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui/utilities'; + +export const arePrefetchViewsLoadedState = createState({ + key: 'arePrefetchViewsLoadedState', + defaultValue: false, +});