-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: defaultHomePagePath to be last visited page or alphatically firs…
…t active object with the name (#6629) ### ISSUE - Closes #6612 - Closes #6125 - Closes #5949 - Closes #6652 ### Description - [x] need to check changes in jest test. - [x] fallback to alphabetically firstActiveObject with the name if no last visited exist https://github.com/user-attachments/assets/dd11480b-c47f-4393-9857-8a55467061e3 - [x] fallback to last visited page with the last visited view by default if no views would have toggled with subNav or viewChangeDropdown it will fallback to INDEX or if no INDEX view then zero position view, works with both subNavViewBar and viewChangeDropdown. https://github.com/user-attachments/assets/33e97e55-2aa2-4c45-a3ab-fc8e43f4964c https://github.com/user-attachments/assets/d1db76a2-da59-4cd2-81bf-d6119408fbbf - [x] lastVisited view across the objects have been persisted so now navigating back from x object to y or z to x will open always last visited view and defaults to index or zero position view. https://github.com/user-attachments/assets/70a01a11-a7ef-4031-926e-02923551466c - [x] lastVisited Page with view has been persisted across the workspace, scope is per workspace so jumping between workspace will also work to have lastvisited object of particular workspace. https://github.com/user-attachments/assets/25107339-8ec1-4421-9f6e-1da43b8f4816 - [x] when lastVisitedObject has been deactivated and going back from settings should have a fallback Object that is alphabetically First activeObject. https://github.com/user-attachments/assets/6b24a933-b139-49ac-82b2-eac5e4848516 - [x] Creation of new View of **anyType** should also get persist and that should get lastVisitedObject with View in case the user leaves from there right away. https://github.com/user-attachments/assets/80ff7114-051d-4e9b-ab58-0e1e3a7d328c - [x] Similarly deleted view also works. https://github.com/user-attachments/assets/cb0b8043-fba4-4a66-941d-b3fa0a57eb22 - [x] fixed active subnav background when opening object directly with root path **/** , it wasn't showing active subNav background. Before: https://github.com/user-attachments/assets/db341c4a-f1f9-43c4-9838-37d1a1f5ab8e Fixed: https://github.com/user-attachments/assets/0f0fd492-bc5d-4efe-b695-bee4e3f41d4e --------- Co-authored-by: Lucas Bordeau <[email protected]>
- Loading branch information
1 parent
5deb0ab
commit 747a154
Showing
21 changed files
with
457 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 0 additions & 31 deletions
31
packages/twenty-front/src/hooks/useDefaultHomePagePath.tsx
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { currentUserState } from '@/auth/states/currentUserState'; | ||
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem'; | ||
import { ObjectPathInfo } from '@/navigation/types/ObjectPathInfo'; | ||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; | ||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; | ||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; | ||
import { AppPath } from '@/types/AppPath'; | ||
import { View } from '@/views/types/View'; | ||
import { useCallback, useMemo } from 'react'; | ||
import { useRecoilValue } from 'recoil'; | ||
import { isDefined } from '~/utils/isDefined'; | ||
|
||
export const useDefaultHomePagePath = () => { | ||
const currentUser = useRecoilValue(currentUserState); | ||
const { activeObjectMetadataItems, alphaSortedActiveObjectMetadataItems } = | ||
useFilteredObjectMetadataItems(); | ||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews); | ||
const { lastVisitedObjectMetadataItemId } = | ||
useLastVisitedObjectMetadataItem(); | ||
|
||
const getActiveObjectMetadataItemMatchingId = useCallback( | ||
(objectMetadataId: string) => { | ||
return activeObjectMetadataItems.find( | ||
(item) => item.id === objectMetadataId, | ||
); | ||
}, | ||
[activeObjectMetadataItems], | ||
); | ||
|
||
const getFirstView = useCallback( | ||
(objectMetadataItemId: string | undefined | null) => | ||
views.find((view) => view.objectMetadataId === objectMetadataItemId), | ||
[views], | ||
); | ||
|
||
const firstObjectPathInfo = useMemo<ObjectPathInfo | null>(() => { | ||
const [firstObjectMetadataItem] = alphaSortedActiveObjectMetadataItems; | ||
|
||
if (!isDefined(firstObjectMetadataItem)) { | ||
return null; | ||
} | ||
|
||
const view = getFirstView(firstObjectMetadataItem?.id); | ||
|
||
return { objectMetadataItem: firstObjectMetadataItem, view }; | ||
}, [alphaSortedActiveObjectMetadataItems, getFirstView]); | ||
|
||
const defaultObjectPathInfo = useMemo<ObjectPathInfo | null>(() => { | ||
if (!isDefined(lastVisitedObjectMetadataItemId)) { | ||
return firstObjectPathInfo; | ||
} | ||
|
||
const lastVisitedObjectMetadataItem = getActiveObjectMetadataItemMatchingId( | ||
lastVisitedObjectMetadataItemId, | ||
); | ||
|
||
if (isDefined(lastVisitedObjectMetadataItem)) { | ||
return { | ||
view: getFirstView(lastVisitedObjectMetadataItemId), | ||
objectMetadataItem: lastVisitedObjectMetadataItem, | ||
}; | ||
} | ||
|
||
return firstObjectPathInfo; | ||
}, [ | ||
firstObjectPathInfo, | ||
getActiveObjectMetadataItemMatchingId, | ||
getFirstView, | ||
lastVisitedObjectMetadataItemId, | ||
]); | ||
|
||
const defaultHomePagePath = useMemo(() => { | ||
if (!isDefined(currentUser)) { | ||
return AppPath.SignInUp; | ||
} | ||
|
||
if (!isDefined(defaultObjectPathInfo)) { | ||
return AppPath.NotFound; | ||
} | ||
|
||
const namePlural = defaultObjectPathInfo.objectMetadataItem?.namePlural; | ||
const viewParam = defaultObjectPathInfo.view | ||
? `?view=${defaultObjectPathInfo.view.id}` | ||
: ''; | ||
|
||
return `/objects/${namePlural}${viewParam}`; | ||
}, [currentUser, defaultObjectPathInfo]); | ||
|
||
return { defaultHomePagePath }; | ||
}; |
66 changes: 66 additions & 0 deletions
66
packages/twenty-front/src/modules/navigation/hooks/useLastVisitedObjectMetadataItem.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; | ||
import { lastVisitedObjectMetadataItemIdStateSelector } from '@/navigation/states/selectors/lastVisitedObjectMetadataItemIdStateSelector'; | ||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; | ||
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-ui'; | ||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; | ||
|
||
export const useLastVisitedObjectMetadataItem = () => { | ||
const currentWorkspace = useRecoilValue(currentWorkspaceState); | ||
const scopeId = currentWorkspace?.id ?? ''; | ||
|
||
const lastVisitedObjectMetadataItemIdState = extractComponentState( | ||
lastVisitedObjectMetadataItemIdStateSelector, | ||
scopeId, | ||
); | ||
|
||
const [lastVisitedObjectMetadataItemId, setLastVisitedObjectMetadataItemId] = | ||
useRecoilState(lastVisitedObjectMetadataItemIdState); | ||
|
||
const { | ||
findActiveObjectMetadataItemBySlug, | ||
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( | ||
`/objects/${newFallbackObjectMetadataItem.namePlural}`, | ||
); | ||
} | ||
}; | ||
|
||
const setLastVisitedObjectMetadataItem = (objectNamePlural: string) => { | ||
const fallbackObjectMetadataItem = | ||
findActiveObjectMetadataItemBySlug(objectNamePlural); | ||
|
||
if (isDefined(fallbackObjectMetadataItem)) { | ||
setLastVisitedObjectMetadataItemId(fallbackObjectMetadataItem.id); | ||
} | ||
}; | ||
|
||
return { | ||
lastVisitedObjectMetadataItemId, | ||
setLastVisitedObjectMetadataItem, | ||
setFallbackForLastVisitedObjectMetadataItem, | ||
}; | ||
}; |
92 changes: 92 additions & 0 deletions
92
packages/twenty-front/src/modules/navigation/hooks/useLastVisitedView.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; | ||
import { lastVisitedObjectMetadataItemIdStateSelector } from '@/navigation/states/selectors/lastVisitedObjectMetadataItemIdStateSelector'; | ||
import { lastVisitedViewPerObjectMetadataItemStateSelector } from '@/navigation/states/selectors/lastVisitedViewPerObjectMetadataItemStateSelector'; | ||
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-ui'; | ||
|
||
export const useLastVisitedView = () => { | ||
const currentWorkspace = useRecoilValue(currentWorkspaceState); | ||
const scopeId = currentWorkspace?.id ?? ''; | ||
|
||
const lastVisitedObjectMetadataItemIdState = extractComponentState( | ||
lastVisitedObjectMetadataItemIdStateSelector, | ||
scopeId, | ||
); | ||
|
||
const lastVisitedViewPerObjectMetadataItemState = extractComponentState( | ||
lastVisitedViewPerObjectMetadataItemStateSelector, | ||
scopeId, | ||
); | ||
|
||
const lastVisitedObjectMetadataItemId = useRecoilValue( | ||
lastVisitedObjectMetadataItemIdState, | ||
); | ||
|
||
const [ | ||
lastVisitedViewPerObjectMetadataItem, | ||
setLastVisitedViewPerObjectMetadataItem, | ||
] = useRecoilState(lastVisitedViewPerObjectMetadataItemState); | ||
|
||
const { findActiveObjectMetadataItemBySlug } = | ||
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 fallbackObjectMetadataItem = | ||
findActiveObjectMetadataItemBySlug(objectNamePlural); | ||
|
||
if (isDefined(fallbackObjectMetadataItem)) { | ||
/* when both are equal meaning there was change in view else | ||
there was a object page change from nav | ||
*/ | ||
const fallbackViewId = | ||
lastVisitedObjectMetadataItemId === fallbackObjectMetadataItem.id | ||
? viewId | ||
: (lastVisitedViewPerObjectMetadataItem?.[ | ||
fallbackObjectMetadataItem.id | ||
] ?? viewId); | ||
|
||
setLastVisitedViewPerObjectMetadataItem({ | ||
[fallbackObjectMetadataItem.id]: fallbackViewId, | ||
}); | ||
} | ||
}; | ||
|
||
const getLastVisitedViewIdFromObjectNamePlural = ( | ||
objectNamePlural: string, | ||
) => { | ||
const objectMetadataItemId: string | undefined = | ||
findActiveObjectMetadataItemBySlug(objectNamePlural)?.id; | ||
return objectMetadataItemId | ||
? lastVisitedViewPerObjectMetadataItem?.[objectMetadataItemId] | ||
: undefined; | ||
}; | ||
|
||
const getLastVisitedViewIdFromObjectMetadataItemId = ( | ||
objectMetadataItemId: string, | ||
) => { | ||
return lastVisitedViewPerObjectMetadataItem?.[objectMetadataItemId]; | ||
}; | ||
return { | ||
setLastVisitedView, | ||
getLastVisitedViewIdFromObjectNamePlural, | ||
getLastVisitedViewIdFromObjectMetadataItemId, | ||
setFallbackForLastVisitedView, | ||
}; | ||
}; |
11 changes: 11 additions & 0 deletions
11
packages/twenty-front/src/modules/navigation/states/lastVisitedObjectMetadataItemIdState.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; | ||
import { localStorageEffect } from '~/utils/recoil-effects'; | ||
|
||
export const lastVisitedObjectMetadataItemIdState = createComponentState<Record< | ||
string, | ||
string | ||
> | null>({ | ||
key: 'lastVisitedObjectMetadataItemIdState', | ||
defaultValue: null, | ||
effects: [localStorageEffect()], | ||
}); |
Oops, something went wrong.