diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/hooks/useReadableNavigationMenuItems.ts b/packages/twenty-front/src/modules/navigation-menu-item/display/hooks/useReadableNavigationMenuItems.ts new file mode 100644 index 0000000000..61949c136e --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/hooks/useReadableNavigationMenuItems.ts @@ -0,0 +1,60 @@ +import { type NavigationMenuItem } from '~/generated-metadata/graphql'; + +import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState'; +import { isNavigationMenuItemFolder } from '@/navigation-menu-item/common/utils/isNavigationMenuItemFolder'; +import { isNavigationMenuItemReadable } from '@/navigation-menu-item/common/utils/isNavigationMenuItemReadable'; +import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; +import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import { viewsSelector } from '@/views/states/selectors/viewsSelector'; + +type UseReadableNavigationMenuItemsArgs = { + topLevelItems: NavigationMenuItem[]; + folderChildrenById: Map; +}; + +export const useReadableNavigationMenuItems = ({ + topLevelItems, + folderChildrenById, +}: UseReadableNavigationMenuItemsArgs) => { + const isLayoutCustomizationModeEnabled = useAtomStateValue( + isLayoutCustomizationModeEnabledState, + ); + const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector); + const views = useAtomStateValue(viewsSelector); + const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); + + const isItemReadable = (item: NavigationMenuItem) => + isNavigationMenuItemReadable({ + item, + objectMetadataItems, + views, + objectPermissionsByObjectMetadataId, + }); + + const filteredFolderChildrenById = new Map(); + for (const [folderId, children] of folderChildrenById) { + filteredFolderChildrenById.set(folderId, children.filter(isItemReadable)); + } + + const filteredTopLevelItems = topLevelItems.filter((item) => + isNavigationMenuItemFolder(item) + ? (filteredFolderChildrenById.get(item.id) ?? []).length > 0 + : isItemReadable(item), + ); + + const displayTopLevelItems = isLayoutCustomizationModeEnabled + ? topLevelItems + : filteredTopLevelItems; + const displayFolderChildrenById = isLayoutCustomizationModeEnabled + ? folderChildrenById + : filteredFolderChildrenById; + + return { + isItemReadable, + filteredTopLevelItems, + filteredFolderChildrenById, + displayTopLevelItems, + displayFolderChildrenById, + }; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/sections/favorites/components/FavoritesSection.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/sections/favorites/components/FavoritesSection.tsx index 489269b4d1..dedb56fe4e 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/sections/favorites/components/FavoritesSection.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/sections/favorites/components/FavoritesSection.tsx @@ -20,6 +20,7 @@ import { NavigationMenuItemDroppableSlot } from '@/navigation-menu-item/display/ import { NavigationMenuItemSortableItem } from '@/navigation-menu-item/display/dnd/components/NavigationMenuItemSortableItem'; import { useIsDropDisabledForSection } from '@/navigation-menu-item/display/dnd/hooks/useIsDropDisabledForSection'; import { useNavigationMenuItemsByFolder } from '@/navigation-menu-item/display/folder/hooks/useNavigationMenuItemsByFolder'; +import { useReadableNavigationMenuItems } from '@/navigation-menu-item/display/hooks/useReadableNavigationMenuItems'; import { useSortedNavigationMenuItems } from '@/navigation-menu-item/display/hooks/useSortedNavigationMenuItems'; import { NavigationMenuItemOrphanDropTarget } from '@/navigation-menu-item/display/sections/components/NavigationMenuItemOrphanDropTarget'; import { NavigationMenuItemSection } from '@/navigation-menu-item/display/sections/components/NavigationMenuItemSection'; @@ -71,12 +72,12 @@ export const FavoritesSection = () => { 'Favorites', ); - const topLevelItems = useMemo( + const allTopLevelItems = useMemo( () => navigationMenuItemsSorted.filter((item) => !item.folderId), [navigationMenuItemsSorted], ); - const folderChildrenById = useMemo(() => { + const allFolderChildrenById = useMemo(() => { const map = new Map(); for (const folder of userNavigationMenuItemsByFolder) { map.set(folder.id, folder.navigationMenuItems); @@ -84,6 +85,14 @@ export const FavoritesSection = () => { return map; }, [userNavigationMenuItemsByFolder]); + const { + displayTopLevelItems: topLevelItems, + displayFolderChildrenById: folderChildrenById, + } = useReadableNavigationMenuItems({ + topLevelItems: allTopLevelItems, + folderChildrenById: allFolderChildrenById, + }); + const folderCount = useMemo( () => topLevelItems.filter(isNavigationMenuItemFolder).length, [topLevelItems], diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionContainer.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionContainer.tsx index e727e91253..bd25a0e73e 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionContainer.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionContainer.tsx @@ -7,9 +7,8 @@ import { type NavigationMenuItem } from '~/generated-metadata/graphql'; import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState'; import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/common/constants/NavigationMenuItemDroppableIds'; import { NavigationDropTargetContext } from '@/navigation-menu-item/common/contexts/NavigationDropTargetContext'; -import { isNavigationMenuItemFolder } from '@/navigation-menu-item/common/utils/isNavigationMenuItemFolder'; -import { isNavigationMenuItemReadable } from '@/navigation-menu-item/common/utils/isNavigationMenuItemReadable'; import { type NavigationMenuItemClickParams } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemSectionItems'; +import { useReadableNavigationMenuItems } from '@/navigation-menu-item/display/hooks/useReadableNavigationMenuItems'; import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/display/object/utils/getObjectMetadataForNavigationMenuItem'; import { NavigationMenuItemSection } from '@/navigation-menu-item/display/sections/components/NavigationMenuItemSection'; import { WorkspaceSectionListReadOnly } from '@/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionListReadOnly'; @@ -17,7 +16,6 @@ import type { EditModeProps } from '@/object-metadata/components/EditModeProps'; import { WorkspaceSectionListEditModeFallback } from '@/object-metadata/components/WorkspaceSectionListEditModeFallback'; import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; -import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { viewsSelector } from '@/views/states/selectors/viewsSelector'; @@ -59,7 +57,6 @@ export const WorkspaceSectionContainer = ({ useNavigationSection('Workspace'); const views = useAtomStateValue(viewsSelector); - const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector); const { addToNavigationFallbackDestination } = useContext( NavigationDropTargetContext, @@ -69,46 +66,27 @@ export const WorkspaceSectionContainer = ({ const isAddToNavigationDropTargetVisible = addToNavigationFallbackDestination?.droppableId === NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS; - const isItemReadable = (item: NavigationMenuItem) => - isNavigationMenuItemReadable({ - item, - objectMetadataItems, - views, - objectPermissionsByObjectMetadataId, - }); - const { folderChildrenById, filteredFolderChildrenById } = items.reduce<{ - folderChildrenById: Map; - filteredFolderChildrenById: Map; - }>( + const folderChildrenById = items.reduce>( (acc, item) => { const folderId = item.folderId; if (isDefined(folderId)) { - const children = acc.folderChildrenById.get(folderId) ?? []; + const children = acc.get(folderId) ?? []; children.push(item); - acc.folderChildrenById.set(folderId, children); - - if (isItemReadable(item)) { - const readableChildren = - acc.filteredFolderChildrenById.get(folderId) ?? []; - readableChildren.push(item); - acc.filteredFolderChildrenById.set(folderId, readableChildren); - } + acc.set(folderId, children); } return acc; }, - { - folderChildrenById: new Map(), - filteredFolderChildrenById: new Map(), - }, + new Map(), ); - const filteredItems = flatItems.filter((item) => { - if (isNavigationMenuItemFolder(item)) { - return (filteredFolderChildrenById.get(item.id) ?? []).length > 0; - } - return isItemReadable(item); - }); + const { filteredTopLevelItems, filteredFolderChildrenById } = + useReadableNavigationMenuItems({ + topLevelItems: flatItems, + folderChildrenById, + }); + + const filteredItems = filteredTopLevelItems; const workspaceOrphanItemsForSection = isLayoutCustomizationModeEnabled ? flatItems