Fix view favorite making the app crash (#15517)
On new workspaces, as they come without the "view" object which not part of engine metadata, favorites pointing to views make the app crashes. This fixes it
This commit is contained in:
committed by
Charles Bochet
parent
4c93c90ea1
commit
087a4e384d
+11
-4
@@ -1,6 +1,5 @@
|
||||
import { FavoriteFolderNavigationDrawerItemDropdown } from '@/favorites/components/FavoriteFolderNavigationDrawerItemDropdown';
|
||||
import { FavoriteIcon } from '@/favorites/components/FavoriteIcon';
|
||||
import { FavoriteNavigationDrawerSubItem } from '@/favorites/components/FavoriteNavigationDrawerSubItem';
|
||||
import { FavoritesDroppable } from '@/favorites/components/FavoritesDroppable';
|
||||
import { FAVORITE_FOLDER_DELETE_MODAL_ID } from '@/favorites/constants/FavoriteFolderDeleteModalId';
|
||||
import { FavoritesDragContext } from '@/favorites/contexts/FavoritesDragContext';
|
||||
@@ -8,8 +7,10 @@ import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useDeleteFavoriteFolder } from '@/favorites/hooks/useDeleteFavoriteFolder';
|
||||
import { useRenameFavoriteFolder } from '@/favorites/hooks/useRenameFavoriteFolder';
|
||||
import { openFavoriteFolderIdsState } from '@/favorites/states/openFavoriteFolderIdsState';
|
||||
import { getFavoriteSecondaryLabel } from '@/favorites/utils/getFavoriteSecondaryLabel';
|
||||
import { isLocationMatchingFavorite } from '@/favorites/utils/isLocationMatchingFavorite';
|
||||
import { type ProcessedFavorite } from '@/favorites/utils/sortFavorites';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
@@ -19,13 +20,14 @@ import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpe
|
||||
import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
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 { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { useContext, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { IconFolder, IconFolderOpen, IconHeartOff } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
@@ -43,6 +45,7 @@ export const CurrentWorkspaceMemberFavorites = ({
|
||||
folder,
|
||||
isGroup,
|
||||
}: CurrentWorkspaceMemberFavoritesProps) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const currentPath = useLocation().pathname;
|
||||
const currentViewPath = useLocation().pathname + useLocation().search;
|
||||
const { isDragging } = useContext(FavoritesDragContext);
|
||||
@@ -196,8 +199,12 @@ export const CurrentWorkspaceMemberFavorites = ({
|
||||
index={index}
|
||||
isInsideScrollableContainer
|
||||
itemComponent={
|
||||
<FavoriteNavigationDrawerSubItem
|
||||
favorite={favorite}
|
||||
<NavigationDrawerSubItem
|
||||
secondaryLabel={getFavoriteSecondaryLabel({
|
||||
objectMetadataItems,
|
||||
favoriteObjectNameSingular:
|
||||
favorite.objectNameSingular,
|
||||
})}
|
||||
label={favorite.labelIdentifier}
|
||||
Icon={() => <FavoriteIcon favorite={favorite} />}
|
||||
to={isDragging ? undefined : favorite.link}
|
||||
|
||||
+10
-3
@@ -1,14 +1,17 @@
|
||||
import { FavoriteIcon } from '@/favorites/components/FavoriteIcon';
|
||||
import { FavoriteNavigationDrawerItem } from '@/favorites/components/FavoriteNavigationDrawerItem';
|
||||
import { FavoritesDroppable } from '@/favorites/components/FavoritesDroppable';
|
||||
import { FavoritesDragContext } from '@/favorites/contexts/FavoritesDragContext';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { getFavoriteSecondaryLabel } from '@/favorites/utils/getFavoriteSecondaryLabel';
|
||||
import { isLocationMatchingFavorite } from '@/favorites/utils/isLocationMatchingFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import styled from '@emotion/styled';
|
||||
import { useContext } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconHeartOff } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
|
||||
@@ -21,6 +24,7 @@ const StyledOrphanFavoritesContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const CurrentWorkspaceMemberOrphanFavorites = () => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
const currentPath = useLocation().pathname;
|
||||
@@ -42,8 +46,11 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => {
|
||||
isInsideScrollableContainer={true}
|
||||
itemComponent={
|
||||
<StyledOrphanFavoritesContainer>
|
||||
<FavoriteNavigationDrawerItem
|
||||
favorite={favorite}
|
||||
<NavigationDrawerItem
|
||||
secondaryLabel={getFavoriteSecondaryLabel({
|
||||
objectMetadataItems,
|
||||
favoriteObjectNameSingular: favorite.objectNameSingular,
|
||||
})}
|
||||
label={favorite.labelIdentifier}
|
||||
Icon={() => <FavoriteIcon favorite={favorite} />}
|
||||
active={isLocationMatchingFavorite(
|
||||
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
import { type ProcessedFavorite } from '@/favorites/utils/sortFavorites';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
|
||||
type FavoriteNavigationDrawerItemProps = {
|
||||
favorite: ProcessedFavorite;
|
||||
label: string;
|
||||
Icon?: React.ComponentProps<typeof NavigationDrawerItem>['Icon'];
|
||||
to?: string;
|
||||
active?: boolean;
|
||||
rightOptions?: React.ComponentProps<
|
||||
typeof NavigationDrawerItem
|
||||
>['rightOptions'];
|
||||
isDragging?: boolean;
|
||||
triggerEvent?: React.ComponentProps<
|
||||
typeof NavigationDrawerItem
|
||||
>['triggerEvent'];
|
||||
};
|
||||
|
||||
export const FavoriteNavigationDrawerItem = ({
|
||||
favorite,
|
||||
label,
|
||||
Icon,
|
||||
to,
|
||||
active,
|
||||
rightOptions,
|
||||
isDragging,
|
||||
triggerEvent,
|
||||
}: FavoriteNavigationDrawerItemProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: favorite.objectNameSingular,
|
||||
});
|
||||
|
||||
if (!favorite.objectNameSingular) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationDrawerItem
|
||||
label={label}
|
||||
secondaryLabel={objectMetadataItem.labelSingular}
|
||||
Icon={Icon}
|
||||
to={to}
|
||||
active={active}
|
||||
rightOptions={rightOptions}
|
||||
isDragging={isDragging}
|
||||
triggerEvent={triggerEvent}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
import { type ProcessedFavorite } from '@/favorites/utils/sortFavorites';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
|
||||
|
||||
type FavoriteNavigationDrawerSubItemProps = {
|
||||
favorite: ProcessedFavorite;
|
||||
label: string;
|
||||
Icon?: React.ComponentProps<typeof NavigationDrawerSubItem>['Icon'];
|
||||
to?: string;
|
||||
active?: boolean;
|
||||
subItemState?: React.ComponentProps<
|
||||
typeof NavigationDrawerSubItem
|
||||
>['subItemState'];
|
||||
rightOptions?: React.ComponentProps<
|
||||
typeof NavigationDrawerSubItem
|
||||
>['rightOptions'];
|
||||
isDragging?: boolean;
|
||||
triggerEvent?: React.ComponentProps<
|
||||
typeof NavigationDrawerSubItem
|
||||
>['triggerEvent'];
|
||||
};
|
||||
|
||||
export const FavoriteNavigationDrawerSubItem = ({
|
||||
favorite,
|
||||
label,
|
||||
Icon,
|
||||
to,
|
||||
active,
|
||||
subItemState,
|
||||
rightOptions,
|
||||
isDragging,
|
||||
triggerEvent,
|
||||
}: FavoriteNavigationDrawerSubItemProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: favorite.objectNameSingular || '',
|
||||
});
|
||||
|
||||
if (!favorite.objectNameSingular) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationDrawerSubItem
|
||||
label={label}
|
||||
secondaryLabel={objectMetadataItem.labelSingular}
|
||||
Icon={Icon}
|
||||
to={to}
|
||||
active={active}
|
||||
subItemState={subItemState}
|
||||
rightOptions={rightOptions}
|
||||
isDragging={isDragging}
|
||||
triggerEvent={triggerEvent}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import { getFavoriteSecondaryLabel } from '../getFavoriteSecondaryLabel';
|
||||
|
||||
describe('getFavoriteSecondaryLabel', () => {
|
||||
it('should return "View" for view object', () => {
|
||||
const result = getFavoriteSecondaryLabel({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
favoriteObjectNameSingular: 'view',
|
||||
});
|
||||
|
||||
expect(result).toBe('View');
|
||||
});
|
||||
|
||||
it('should return labelSingular for matching object metadata item', () => {
|
||||
const result = getFavoriteSecondaryLabel({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
favoriteObjectNameSingular: 'person',
|
||||
});
|
||||
|
||||
expect(result).toBe('Person');
|
||||
});
|
||||
|
||||
it('should return undefined when object metadata item is not found', () => {
|
||||
const result = getFavoriteSecondaryLabel({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
favoriteObjectNameSingular: 'nonexistent',
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type GetFavoriteSecondaryLabelProps = {
|
||||
objectMetadataItems: Pick<
|
||||
ObjectMetadataItem,
|
||||
'nameSingular' | 'labelSingular'
|
||||
>[];
|
||||
favoriteObjectNameSingular: string;
|
||||
};
|
||||
|
||||
export const getFavoriteSecondaryLabel = ({
|
||||
objectMetadataItems,
|
||||
favoriteObjectNameSingular,
|
||||
}: GetFavoriteSecondaryLabelProps) => {
|
||||
if (favoriteObjectNameSingular === 'view') {
|
||||
return 'View';
|
||||
}
|
||||
|
||||
return objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular === favoriteObjectNameSingular,
|
||||
)?.labelSingular;
|
||||
};
|
||||
Reference in New Issue
Block a user