favorite folders followup (#8570)

Something changed, which affected the Favorite folder picker checkbox
styles -- fixed it!
Cleaned up code in `CurrentWorkspaceMemberFavoritesFolders` - removed
redundant filtering since favorites are already filtered in
`usePrefetchedFavoritesData`.
Regarding issue #8569 - I am not sure what to do in this case. Since
Folders data is gated by a feature flag, we can't use it in
`CurrentWorkspaceMemberFavoritesFolders` to ensure the favorite section
renders with empty folders. Currently, the section only appears when at
least one favorite exists - may be leave this section open at all times
or fix this bug after removal of the feature flag?

---------

Co-authored-by: Nitin Koche <nitinkoche@Nitins-MacBook-Pro.local>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
nitin
2024-11-19 23:25:25 +05:30
committed by GitHub
parent 2773974459
commit 4f2019edae
45 changed files with 253 additions and 315 deletions

View File

@@ -37,8 +37,8 @@ export const DeleteRecordsActionEffect = ({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
}); });
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const deleteFavorite = useDeleteFavorite(); const { deleteFavorite } = useDeleteFavorite();
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2( const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState, contextStoreNumberOfSelectedRecordsComponentState,

View File

@@ -23,11 +23,11 @@ export const ManageFavoritesActionEffect = ({
contextStoreTargetedRecordsRuleComponentState, contextStoreTargetedRecordsRuleComponentState,
); );
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const createFavorite = useCreateFavorite(); const { createFavorite } = useCreateFavorite();
const deleteFavorite = useDeleteFavorite(); const { deleteFavorite } = useDeleteFavorite();
const selectedRecordId = const selectedRecordId =
contextStoreTargetedRecordsRule.mode === 'selection' contextStoreTargetedRecordsRule.mode === 'selection'

View File

@@ -61,9 +61,9 @@ export const CurrentWorkspaceMemberFavorites = ({
const selectedFavoriteIndex = folder.favorites.findIndex((favorite) => const selectedFavoriteIndex = folder.favorites.findIndex((favorite) =>
isLocationMatchingFavorite(currentPath, currentViewPath, favorite), isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
); );
const handleReorderFavorite = useReorderFavorite(); const { handleReorderFavorite } = useReorderFavorite();
const deleteFavorite = useDeleteFavorite(); const { deleteFavorite } = useDeleteFavorite();
const favoriteFolderContentLength = folder.favorites.length; const favoriteFolderContentLength = folder.favorites.length;
@@ -154,6 +154,7 @@ export const CurrentWorkspaceMemberFavorites = ({
key={favorite.id} key={favorite.id}
draggableId={favorite.id} draggableId={favorite.id}
index={index} index={index}
isInsideScrollableContainer
itemComponent={ itemComponent={
<NavigationDrawerSubItem <NavigationDrawerSubItem
key={favorite.id} key={favorite.id}

View File

@@ -4,8 +4,8 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { import {
IconFolderPlus, IconFolderPlus,
IconHeartOff, IconHeartOff,
isDefined,
LightIconButton, LightIconButton,
isDefined,
} from 'twenty-ui'; } from 'twenty-ui';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
@@ -32,9 +32,9 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
const currentViewPath = useLocation().pathname + useLocation().search; const currentViewPath = useLocation().pathname + useLocation().search;
const theme = useTheme(); const theme = useTheme();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const deleteFavorite = useDeleteFavorite(); const { deleteFavorite } = useDeleteFavorite();
const handleReorderFavorite = useReorderFavorite(); const { handleReorderFavorite } = useReorderFavorite();
const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] = const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
useRecoilState(isFavoriteFolderCreatingState); useRecoilState(isFavoriteFolderCreatingState);
@@ -50,24 +50,25 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
const toggleNewFolder = () => { const toggleNewFolder = () => {
setIsFavoriteFolderCreating((current) => !current); setIsFavoriteFolderCreating((current) => !current);
}; };
const shouldDisplayFavoritesWithFeatureFlagEnabled = true;
//todo: remove this logic once feature flag gating is removed
const shouldDisplayFavoritesWithoutFeatureFlagEnabled =
favorites.length > 0 || isFavoriteFolderCreating;
const shouldDisplayFavorites = isFavoriteFolderEnabled
? shouldDisplayFavoritesWithFeatureFlagEnabled
: shouldDisplayFavoritesWithoutFeatureFlagEnabled;
if (loading && isDefined(currentWorkspaceMember)) { if (loading && isDefined(currentWorkspaceMember)) {
return <FavoritesSkeletonLoader />; return <FavoritesSkeletonLoader />;
} }
const currentWorkspaceMemberFavorites = favorites.filter( const orphanFavorites = favorites.filter(
(favorite) => favorite.workspaceMemberId === currentWorkspaceMember?.id,
);
const orphanFavorites = currentWorkspaceMemberFavorites.filter(
(favorite) => !favorite.favoriteFolderId, (favorite) => !favorite.favoriteFolderId,
); );
if ( if (!shouldDisplayFavorites) {
(!currentWorkspaceMemberFavorites ||
currentWorkspaceMemberFavorites.length === 0) &&
!isFavoriteFolderCreating
) {
return null; return null;
} }
@@ -104,6 +105,7 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
key={favorite.id} key={favorite.id}
draggableId={favorite.id} draggableId={favorite.id}
index={index} index={index}
isInsideScrollableContainer={true}
itemComponent={ itemComponent={
<NavigationDrawerItem <NavigationDrawerItem
key={favorite.id} key={favorite.id}

View File

@@ -38,6 +38,7 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
dropdownHotkeyScope={{ dropdownHotkeyScope={{
scope: FavoriteFolderHotkeyScope.FavoriteFolderRightIconDropdown, scope: FavoriteFolderHotkeyScope.FavoriteFolderRightIconDropdown,
}} }}
usePortal
data-select-disable data-select-disable
clickableComponent={ clickableComponent={
<LightIconButton Icon={IconDotsVertical} accent="tertiary" /> <LightIconButton Icon={IconDotsVertical} accent="tertiary" />

View File

@@ -18,8 +18,8 @@ export const FavoriteFolders = ({
}: FavoriteFoldersProps) => { }: FavoriteFoldersProps) => {
const [newFolderName, setNewFolderName] = useState(''); const [newFolderName, setNewFolderName] = useState('');
const favoritesByFolder = useFavoritesByFolder(); const { favoritesByFolder } = useFavoritesByFolder();
const createFavoriteFolder = useCreateFavoriteFolder(); const { createNewFavoriteFolder } = useCreateFavoriteFolder();
const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] = const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
useRecoilState(isFavoriteFolderCreatingState); useRecoilState(isFavoriteFolderCreatingState);
@@ -33,12 +33,12 @@ export const FavoriteFolders = ({
setIsFavoriteFolderCreating(false); setIsFavoriteFolderCreating(false);
setNewFolderName(''); setNewFolderName('');
await createFavoriteFolder(value); await createNewFavoriteFolder(value);
return true; return true;
}; };
const handleClickOutside = async ( const handleClickOutside = async (
event: MouseEvent | TouchEvent, _event: MouseEvent | TouchEvent,
value: string, value: string,
) => { ) => {
if (!value) { if (!value) {
@@ -48,7 +48,7 @@ export const FavoriteFolders = ({
setIsFavoriteFolderCreating(false); setIsFavoriteFolderCreating(false);
setNewFolderName(''); setNewFolderName('');
await createFavoriteFolder(value); await createNewFavoriteFolder(value);
}; };
const handleCancelFavoriteFolderCreation = () => { const handleCancelFavoriteFolderCreation = () => {

View File

@@ -1,11 +1,11 @@
import { PageFavoriteButton } from '@/favorites/components/PageFavoriteButton';
import { FavoriteFolderPicker } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPicker'; import { FavoriteFolderPicker } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPicker';
import { FavoriteFolderPickerEffect } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPickerEffect'; import { FavoriteFolderPickerEffect } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPickerEffect';
import { FavoriteFolderPickerScope } from '@/favorites/favorite-folder-picker/scopes/FavoriteFolderPickerScope'; import { FavoriteFolderPickerComponentInstanceContext } from '@/favorites/favorite-folder-picker/scopes/FavoriteFolderPickerScope';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { PageFavoriteButton } from '@/ui/layout/page/components/PageFavoriteButton';
type PageFavoriteFoldersDropdownProps = { type PageFavoriteFoldersDropdownProps = {
dropdownId: string; dropdownId: string;
@@ -23,7 +23,9 @@ export const PageFavoriteFoldersDropdown = ({
const { closeDropdown } = useDropdown(dropdownId); const { closeDropdown } = useDropdown(dropdownId);
return ( return (
<FavoriteFolderPickerScope favoriteFoldersScopeId={dropdownId}> <FavoriteFolderPickerComponentInstanceContext
favoriteFoldersScopeId={dropdownId}
>
<DropdownScope dropdownScopeId={dropdownId}> <DropdownScope dropdownScopeId={dropdownId}>
<Dropdown <Dropdown
dropdownId={dropdownId} dropdownId={dropdownId}
@@ -44,6 +46,6 @@ export const PageFavoriteFoldersDropdown = ({
}} }}
/> />
</DropdownScope> </DropdownScope>
</FavoriteFolderPickerScope> </FavoriteFolderPickerComponentInstanceContext>
); );
}; };

View File

@@ -1,10 +0,0 @@
import styled from '@emotion/styled';
import { MenuItemMultiSelect } from '@ui/navigation/menu-item/components/MenuItemMultiSelect';
const StyledNoGapMenuItem = styled(MenuItemMultiSelect)`
& > div {
gap: 0;
}
`;
export const FavoriteFolderMenuItemMultiSelect = StyledNoGapMenuItem;

View File

@@ -36,7 +36,7 @@ export const FavoriteFolderPicker = ({
FavoriteFolderPickerInstanceContext, FavoriteFolderPickerInstanceContext,
); );
const { getFoldersByIds, toggleFolderSelection } = useFavoriteFolderPicker({ const { favoriteFolders, toggleFolderSelection } = useFavoriteFolderPicker({
record, record,
objectNameSingular, objectNameSingular,
}); });
@@ -45,8 +45,7 @@ export const FavoriteFolderPicker = ({
favoriteFolderSearchFilterComponentState, favoriteFolderSearchFilterComponentState,
); );
const folders = getFoldersByIds(); const filteredFolders = favoriteFolders.filter((folder) =>
const filteredFolders = folders.filter((folder) =>
folder.name folder.name
.toLowerCase() .toLowerCase()
.includes(favoriteFoldersSearchFilter.toLowerCase()), .includes(favoriteFoldersSearchFilter.toLowerCase()),
@@ -94,7 +93,7 @@ export const FavoriteFolderPicker = ({
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight> <DropdownMenuItemsContainer hasMaxHeight>
<FavoriteFolderPickerList <FavoriteFolderPickerList
folders={folders} folders={favoriteFolders}
toggleFolderSelection={toggleFolderSelection} toggleFolderSelection={toggleFolderSelection}
/> />
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>

View File

@@ -30,7 +30,7 @@ export const FavoriteFolderPickerEffect = ({
const { favoriteFolders } = usePrefetchedFavoritesFoldersData(); const { favoriteFolders } = usePrefetchedFavoritesFoldersData();
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const setCheckedState = useSetRecoilComponentStateV2( const setCheckedState = useSetRecoilComponentStateV2(
favoriteFolderPickerCheckedComponentState, favoriteFolderPickerCheckedComponentState,
); );

View File

@@ -14,10 +14,6 @@ const StyledFooter = styled.div`
border-top: 1px solid ${({ theme }) => theme.border.color.light}; border-top: 1px solid ${({ theme }) => theme.border.color.light};
`; `;
const StyledIconPlus = styled(IconPlus)`
padding-left: ${({ theme }) => theme.spacing(1)};
`;
export const FavoriteFolderPickerFooter = () => { export const FavoriteFolderPickerFooter = () => {
const [, setIsFavoriteFolderCreating] = useRecoilState( const [, setIsFavoriteFolderCreating] = useRecoilState(
isFavoriteFolderCreatingState, isFavoriteFolderCreatingState,
@@ -35,7 +31,7 @@ export const FavoriteFolderPickerFooter = () => {
closeDropdown(); closeDropdown();
}} }}
text="Add folder" text="Add folder"
LeftIcon={() => <StyledIconPlus size={theme.icon.size.md} />} LeftIcon={() => <IconPlus size={theme.icon.size.md} />}
/> />
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>
</StyledFooter> </StyledFooter>

View File

@@ -4,8 +4,7 @@ import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { MenuItem } from 'twenty-ui'; import { MenuItem, MenuItemMultiSelect } from 'twenty-ui';
import { FavoriteFolderMenuItemMultiSelect } from './FavoriteFolderMenuItemMultiSelect';
const StyledItemsContainer = styled.div` const StyledItemsContainer = styled.div`
width: 100%; width: 100%;
@@ -30,6 +29,7 @@ export const FavoriteFolderPickerList = ({
const [favoriteFoldersSearchFilter] = useRecoilComponentStateV2( const [favoriteFoldersSearchFilter] = useRecoilComponentStateV2(
favoriteFolderSearchFilterComponentState, favoriteFolderSearchFilterComponentState,
); );
const [favoriteFolderPickerChecked] = useRecoilComponentStateV2( const [favoriteFolderPickerChecked] = useRecoilComponentStateV2(
favoriteFolderPickerCheckedComponentState, favoriteFolderPickerCheckedComponentState,
); );
@@ -47,7 +47,7 @@ export const FavoriteFolderPickerList = ({
return ( return (
<StyledItemsContainer> <StyledItemsContainer>
{showNoFolderOption && ( {showNoFolderOption && (
<FavoriteFolderMenuItemMultiSelect <MenuItemMultiSelect
key={`menu-${NO_FOLDER_ID}`} key={`menu-${NO_FOLDER_ID}`}
onSelectChange={() => toggleFolderSelection(NO_FOLDER_ID)} onSelectChange={() => toggleFolderSelection(NO_FOLDER_ID)}
selected={favoriteFolderPickerChecked.includes(NO_FOLDER_ID)} selected={favoriteFolderPickerChecked.includes(NO_FOLDER_ID)}
@@ -60,7 +60,7 @@ export const FavoriteFolderPickerList = ({
)} )}
{filteredFolders.length > 0 {filteredFolders.length > 0
? filteredFolders.map((folder) => ( ? filteredFolders.map((folder) => (
<FavoriteFolderMenuItemMultiSelect <MenuItemMultiSelect
key={`menu-${folder.id}`} key={`menu-${folder.id}`}
onSelectChange={() => toggleFolderSelection(folder.id)} onSelectChange={() => toggleFolderSelection(folder.id)}
selected={favoriteFolderPickerChecked.includes(folder.id)} selected={favoriteFolderPickerChecked.includes(folder.id)}

View File

@@ -1,6 +1,5 @@
import { favoriteFolderIdsPickerComponentState } from '@/favorites/favorite-folder-picker/states/favoriteFolderIdPickerComponentState';
import { favoriteFolderPickerCheckedComponentState } from '@/favorites/favorite-folder-picker/states/favoriteFolderPickerCheckedComponentState'; import { favoriteFolderPickerCheckedComponentState } from '@/favorites/favorite-folder-picker/states/favoriteFolderPickerCheckedComponentState';
import { favoriteFolderPickerComponentFamilyState } from '@/favorites/favorite-folder-picker/states/favoriteFolderPickerComponentFamilyState'; import { favoriteFoldersComponentSelector } from '@/favorites/favorite-folder-picker/states/selectors/favoriteFoldersComponentSelector';
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite'; import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite'; import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
@@ -8,7 +7,7 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder'; import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
@@ -17,46 +16,26 @@ type useFavoriteFolderPickerProps = {
objectNameSingular: string; objectNameSingular: string;
}; };
type FolderOperations = { type useFavoriteFolderPickerReturnType = {
getFoldersByIds: () => FavoriteFolder[]; favoriteFolders: FavoriteFolder[];
toggleFolderSelection: (folderId: string) => Promise<void>; toggleFolderSelection: (folderId: string) => Promise<void>;
}; };
export const useFavoriteFolderPicker = ({ export const useFavoriteFolderPicker = ({
record, record,
objectNameSingular, objectNameSingular,
}: useFavoriteFolderPickerProps): FolderOperations => { }: useFavoriteFolderPickerProps): useFavoriteFolderPickerReturnType => {
const [favoriteFolderIdsPicker] = useRecoilComponentStateV2(
favoriteFolderIdsPickerComponentState,
);
const favoriteFoldersMultiSelectCheckedState = const favoriteFoldersMultiSelectCheckedState =
useRecoilComponentCallbackStateV2( useRecoilComponentCallbackStateV2(
favoriteFolderPickerCheckedComponentState, favoriteFolderPickerCheckedComponentState,
); );
const favoriteFolderPickerFamilyState = useRecoilComponentCallbackStateV2( const { sortedFavorites: favorites } = useFavorites();
favoriteFolderPickerComponentFamilyState, const { createFavorite } = useCreateFavorite();
); const { deleteFavorite } = useDeleteFavorite();
const favorites = useFavorites(); const favoriteFolders = useRecoilComponentValueV2(
const createFavorite = useCreateFavorite(); favoriteFoldersComponentSelector,
const deleteFavorite = useDeleteFavorite();
const getFoldersByIds = useRecoilCallback(
({ snapshot }) =>
(): FavoriteFolder[] => {
return favoriteFolderIdsPicker
.map((folderId) => {
const folderValue = snapshot
.getLoadable(favoriteFolderPickerFamilyState(folderId))
.getValue();
return folderValue;
})
.filter((folder): folder is FavoriteFolder => isDefined(folder));
},
[favoriteFolderIdsPicker, favoriteFolderPickerFamilyState],
); );
const toggleFolderSelection = useRecoilCallback( const toggleFolderSelection = useRecoilCallback(
@@ -123,7 +102,7 @@ export const useFavoriteFolderPicker = ({
); );
return { return {
getFoldersByIds, favoriteFolders,
toggleFolderSelection, toggleFolderSelection,
}; };
}; };

View File

@@ -1,15 +1,15 @@
import { FavoriteFolderPickerInstanceContext } from '@/favorites/favorite-folder-picker/states/context/FavoriteFolderPickerInstanceContext'; import { FavoriteFolderPickerInstanceContext } from '@/favorites/favorite-folder-picker/states/context/FavoriteFolderPickerInstanceContext';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
type FavoriteFolderPickerScopeProps = { type FavoriteFolderPickerComponentInstanceContextProps = {
children: ReactNode; children: ReactNode;
favoriteFoldersScopeId: string; favoriteFoldersScopeId: string;
}; };
export const FavoriteFolderPickerScope = ({ export const FavoriteFolderPickerComponentInstanceContext = ({
children, children,
favoriteFoldersScopeId, favoriteFoldersScopeId,
}: FavoriteFolderPickerScopeProps) => { }: FavoriteFolderPickerComponentInstanceContextProps) => {
return ( return (
<FavoriteFolderPickerInstanceContext.Provider <FavoriteFolderPickerInstanceContext.Provider
value={{ instanceId: favoriteFoldersScopeId }} value={{ instanceId: favoriteFoldersScopeId }}

View File

@@ -1,9 +0,0 @@
import { FavoriteFolderPickerInstanceContext } from '@/favorites/favorite-folder-picker/states/context/FavoriteFolderPickerInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const favoriteFolderLoadingComponentState =
createComponentStateV2<boolean>({
key: 'favoriteFoldersLoadingComponentState',
defaultValue: false,
componentInstanceContext: FavoriteFolderPickerInstanceContext,
});

View File

@@ -0,0 +1,31 @@
import { FavoriteFolderPickerInstanceContext } from '@/favorites/favorite-folder-picker/states/context/FavoriteFolderPickerInstanceContext';
import { favoriteFolderIdsPickerComponentState } from '@/favorites/favorite-folder-picker/states/favoriteFolderIdPickerComponentState';
import { favoriteFolderPickerComponentFamilyState } from '@/favorites/favorite-folder-picker/states/favoriteFolderPickerComponentFamilyState';
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { isDefined } from 'twenty-ui';
export const favoriteFoldersComponentSelector = createComponentSelectorV2<
FavoriteFolder[]
>({
key: 'favoriteFoldersComponentSelector',
componentInstanceContext: FavoriteFolderPickerInstanceContext,
get:
({ instanceId }) =>
({ get }) => {
const folderIds = get(
favoriteFolderIdsPickerComponentState.atomFamily({ instanceId }),
);
return folderIds
.map((folderId: string) =>
get(
favoriteFolderPickerComponentFamilyState.atomFamily({
instanceId,
familyKey: folderId,
}),
),
)
.filter((folder): folder is FavoriteFolder => isDefined(folder));
},
});

View File

@@ -44,7 +44,10 @@ describe('useCreateFavorite', () => {
{ wrapper: Wrapper }, { wrapper: Wrapper },
); );
result.current(favoriteTargetObjectRecord, CoreObjectNameSingular.Person); result.current.createFavorite(
favoriteTargetObjectRecord,
CoreObjectNameSingular.Person,
);
await waitFor(() => { await waitFor(() => {
expect(mocks[0].result).toHaveBeenCalled(); expect(mocks[0].result).toHaveBeenCalled();

View File

@@ -38,7 +38,7 @@ describe('useDeleteFavorite', () => {
{ wrapper: Wrapper }, { wrapper: Wrapper },
); );
result.current(favoriteId); result.current.deleteFavorite(favoriteId);
await waitFor(() => { await waitFor(() => {
expect(mocks[1].result).toHaveBeenCalled(); expect(mocks[1].result).toHaveBeenCalled();

View File

@@ -38,6 +38,6 @@ describe('useFavorites', () => {
{ wrapper: Wrapper }, { wrapper: Wrapper },
); );
expect(result.current).toEqual(sortedFavorites); expect(result.current.sortedFavorites).toEqual(sortedFavorites);
}); });
}); });

View File

@@ -54,7 +54,10 @@ describe('useReorderFavorite', () => {
announce: () => {}, announce: () => {},
}; };
result.current(dragAndDropResult, responderProvided); result.current.handleReorderFavorite(
dragAndDropResult,
responderProvided,
);
}); });
await waitFor(() => { await waitFor(() => {

View File

@@ -34,5 +34,5 @@ export const useCreateFavorite = () => {
}); });
}; };
return createFavorite; return { createFavorite };
}; };

View File

@@ -28,5 +28,5 @@ export const useCreateFavoriteFolder = () => {
}); });
}; };
return createNewFavoriteFolder; return { createNewFavoriteFolder };
}; };

View File

@@ -10,5 +10,5 @@ export const useDeleteFavorite = () => {
deleteOneRecord(favoriteId); deleteOneRecord(favoriteId);
}; };
return deleteFavorite; return { deleteFavorite };
}; };

View File

@@ -1,23 +1,46 @@
import { Favorite } from '@/favorites/types/Favorite';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useReadFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useReadFindManyRecordsQueryInCache';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData'; import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
export const useDeleteFavoriteFolder = () => { export const useDeleteFavoriteFolder = () => {
const { deleteOneRecord } = useDeleteOneRecord({ const { deleteOneRecord } = useDeleteOneRecord({
objectNameSingular: CoreObjectNameSingular.FavoriteFolder, objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
}); });
const { upsertFavorites, favorites, workspaceFavorites } =
usePrefetchedFavoritesData(); const { upsertRecordsInCache } = usePrefetchRunQuery<Favorite>({
prefetchKey: PrefetchKey.AllFavorites,
});
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular:
PREFETCH_CONFIG[PrefetchKey.AllFavorites].objectNameSingular,
});
const { readFindManyRecordsQueryInCache } =
useReadFindManyRecordsQueryInCache({
objectMetadataItem,
});
const deleteFavoriteFolder = async (folderId: string): Promise<void> => { const deleteFavoriteFolder = async (folderId: string): Promise<void> => {
await deleteOneRecord(folderId); await deleteOneRecord(folderId);
const updatedFavorites = [ const allFavorites = readFindManyRecordsQueryInCache<Favorite>({
...favorites.filter((favorite) => favorite.favoriteFolderId !== folderId), queryVariables: {},
...workspaceFavorites, recordGqlFields: PREFETCH_CONFIG[
]; PrefetchKey.AllFavorites
].operationSignatureFactory({ objectMetadataItem }).fields,
});
upsertFavorites(updatedFavorites); const updatedFavorites = allFavorites.filter(
(favorite) => favorite.favoriteFolderId !== folderId,
);
upsertRecordsInCache(updatedFavorites);
}; };
return { return {

View File

@@ -52,5 +52,5 @@ export const useFavorites = () => {
], ],
); );
return sortedFavorites; return { sortedFavorites };
}; };

View File

@@ -1,62 +1,30 @@
import { sortFavorites } from '@/favorites/utils/sortFavorites'; import { sortFavorites } from '@/favorites/utils/sortFavorites';
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular'; import { useFavoritesMetadata } from './useFavoritesMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData'; import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
import { usePrefetchedFavoritesFoldersData } from './usePrefetchedFavoritesFoldersData'; import { usePrefetchedFavoritesFoldersData } from './usePrefetchedFavoritesFoldersData';
export const useFavoritesByFolder = () => { export const useFavoritesByFolder = () => {
const { favorites } = usePrefetchedFavoritesData(); const { favorites } = usePrefetchedFavoritesData();
const { favoriteFolders } = usePrefetchedFavoritesFoldersData(); const { favoriteFolders } = usePrefetchedFavoritesFoldersData();
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews); const {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const getObjectRecordIdentifierByNameSingular =
useGetObjectRecordIdentifierByNameSingular();
const { objectMetadataItem: favoriteObjectMetadataItem } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.Favorite,
});
const favoriteRelationFields = useMemo(
() =>
favoriteObjectMetadataItem.fields.filter(
(fieldMetadataItem) =>
fieldMetadataItem.type === FieldMetadataType.Relation &&
fieldMetadataItem.name !== 'workspaceMember' &&
fieldMetadataItem.name !== 'favoriteFolder',
),
[favoriteObjectMetadataItem.fields],
);
const favoritesByFolder = useMemo(() => {
return favoriteFolders.map((folder) => ({
folderId: folder.id,
folderName: folder.name,
favorites: sortFavorites(
favorites.filter((favorite) => favorite.favoriteFolderId === folder.id),
favoriteRelationFields,
getObjectRecordIdentifierByNameSingular,
true,
views,
objectMetadataItems,
),
}));
}, [
favoriteFolders,
favorites,
favoriteRelationFields,
getObjectRecordIdentifierByNameSingular,
views, views,
objectMetadataItems, objectMetadataItems,
]); getObjectRecordIdentifierByNameSingular,
favoriteRelationFields,
} = useFavoritesMetadata();
return favoritesByFolder; const favoritesByFolder = favoriteFolders.map((folder) => ({
folderId: folder.id,
folderName: folder.name,
favorites: sortFavorites(
favorites.filter((favorite) => favorite.favoriteFolderId === folder.id),
favoriteRelationFields,
getObjectRecordIdentifierByNameSingular,
true,
views,
objectMetadataItems,
),
}));
return { favoritesByFolder };
}; };

View File

@@ -0,0 +1,35 @@
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
import { useRecoilValue } from 'recoil';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const useFavoritesMetadata = () => {
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const getObjectRecordIdentifierByNameSingular =
useGetObjectRecordIdentifierByNameSingular();
const { objectMetadataItem: favoriteObjectMetadataItem } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.Favorite,
});
const favoriteRelationFields = favoriteObjectMetadataItem.fields.filter(
(fieldMetadataItem) =>
fieldMetadataItem.type === FieldMetadataType.Relation &&
fieldMetadataItem.name !== 'workspaceMember' &&
fieldMetadataItem.name !== 'favoriteFolder',
);
return {
views,
objectMetadataItems,
getObjectRecordIdentifierByNameSingular,
favoriteRelationFields,
};
};

View File

@@ -12,7 +12,7 @@ export const useReorderFavorite = () => {
objectNameSingular: CoreObjectNameSingular.Favorite, objectNameSingular: CoreObjectNameSingular.Favorite,
}); });
const reorderFavorite: OnDragEndResponder = (result) => { const handleReorderFavorite: OnDragEndResponder = (result) => {
if (!result.destination) return; if (!result.destination) return;
const draggedFavoriteId = result.draggableId; const draggedFavoriteId = result.draggableId;
@@ -37,5 +37,5 @@ export const useReorderFavorite = () => {
}); });
}; };
return reorderFavorite; return { handleReorderFavorite };
}; };

View File

@@ -1,50 +1,28 @@
import { sortFavorites } from '@/favorites/utils/sortFavorites'; import { sortFavorites } from '@/favorites/utils/sortFavorites';
import { useGetObjectRecordIdentifierByNameSingular } from '@/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useRecoilValue } from 'recoil'; import { useFavoritesMetadata } from './useFavoritesMetadata';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData'; import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
export const useSortedFavorites = () => { export const useSortedFavorites = () => {
const { favorites, workspaceFavorites } = usePrefetchedFavoritesData(); const { favorites, workspaceFavorites } = usePrefetchedFavoritesData();
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews); const {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState); views,
const { objectMetadataItem: favoriteObjectMetadataItem } = objectMetadataItems,
useObjectMetadataItem({ getObjectRecordIdentifierByNameSingular,
objectNameSingular: CoreObjectNameSingular.Favorite, favoriteRelationFields,
}); } = useFavoritesMetadata();
const getObjectRecordIdentifierByNameSingular =
useGetObjectRecordIdentifierByNameSingular();
const favoriteRelationFieldMetadataItems = useMemo(
() =>
favoriteObjectMetadataItem.fields.filter(
(fieldMetadataItem) =>
fieldMetadataItem.type === FieldMetadataType.Relation &&
fieldMetadataItem.name !== 'workspaceMember' &&
fieldMetadataItem.name !== 'favoriteFolder',
),
[favoriteObjectMetadataItem.fields],
);
const favoritesSorted = useMemo(() => { const favoritesSorted = useMemo(() => {
return sortFavorites( return sortFavorites(
favorites, favorites,
favoriteRelationFieldMetadataItems, favoriteRelationFields,
getObjectRecordIdentifierByNameSingular, getObjectRecordIdentifierByNameSingular,
true, true,
views, views,
objectMetadataItems, objectMetadataItems,
); );
}, [ }, [
favoriteRelationFieldMetadataItems, favoriteRelationFields,
favorites, favorites,
getObjectRecordIdentifierByNameSingular, getObjectRecordIdentifierByNameSingular,
views, views,
@@ -54,14 +32,14 @@ export const useSortedFavorites = () => {
const workspaceFavoritesSorted = useMemo(() => { const workspaceFavoritesSorted = useMemo(() => {
return sortFavorites( return sortFavorites(
workspaceFavorites.filter((favorite) => favorite.viewId), workspaceFavorites.filter((favorite) => favorite.viewId),
favoriteRelationFieldMetadataItems, favoriteRelationFields,
getObjectRecordIdentifierByNameSingular, getObjectRecordIdentifierByNameSingular,
false, false,
views, views,
objectMetadataItems, objectMetadataItems,
); );
}, [ }, [
favoriteRelationFieldMetadataItems, favoriteRelationFields,
getObjectRecordIdentifierByNameSingular, getObjectRecordIdentifierByNameSingular,
workspaceFavorites, workspaceFavorites,
views, views,

View File

@@ -52,5 +52,5 @@ export const useWorkspaceFavorites = () => {
], ],
); );
return sortedWorkspaceFavorites; return { sortedWorkspaceFavorites };
}; };

View File

@@ -6,23 +6,20 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders'; import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
import { WorkspaceFavorites } from '@/favorites/components/WorkspaceFavorites'; import { WorkspaceFavorites } from '@/favorites/components/WorkspaceFavorites';
import { NavigationDrawerOpenedSection } from '@/object-metadata/components/NavigationDrawerOpenedSection'; import { NavigationDrawerOpenedSection } from '@/object-metadata/components/NavigationDrawerOpenedSection';
import { NavigationDrawerSectionForObjectMetadataItemsWrapper } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsWrapper'; import { RemoteNavigationDrawerSection } from '@/object-metadata/components/RemoteNavigationDrawerSection';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection'; import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded'; import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState'; import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
const StyledMainSection = styled(NavigationDrawerSection)` const StyledMainSection = styled(NavigationDrawerSection)`
min-height: fit-content; min-height: fit-content;
`; `;
const StyledContainer = styled.div`
overflow-x: hidden;
overflow-y: auto;
`;
export const MainNavigationDrawerItems = () => { export const MainNavigationDrawerItems = () => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { toggleCommandMenu } = useCommandMenu(); const { toggleCommandMenu } = useCommandMenu();
@@ -59,15 +56,16 @@ export const MainNavigationDrawerItems = () => {
/> />
</StyledMainSection> </StyledMainSection>
)} )}
<StyledContainer> <ScrollWrapper
contextProviderName="navigationDrawer"
enableXScroll={false}
scrollHide={true}
>
<NavigationDrawerOpenedSection /> <NavigationDrawerOpenedSection />
<CurrentWorkspaceMemberFavoritesFolders /> <CurrentWorkspaceMemberFavoritesFolders />
<WorkspaceFavorites /> <WorkspaceFavorites />
<RemoteNavigationDrawerSection />
<NavigationDrawerSectionForObjectMetadataItemsWrapper isRemote={true} /> </ScrollWrapper>
</StyledContainer>
</> </>
); );
}; };

View File

@@ -7,7 +7,8 @@ import { View } from '@/views/types/View';
export const useFilteredObjectMetadataItemsForWorkspaceFavorites = () => { export const useFilteredObjectMetadataItemsForWorkspaceFavorites = () => {
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews); const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const workspaceFavorites = useWorkspaceFavorites(); const { sortedWorkspaceFavorites: workspaceFavorites } =
useWorkspaceFavorites();
const workspaceFavoriteIds = new Set( const workspaceFavoriteIds = new Set(
workspaceFavorites.map((favorite) => favorite.recordId), workspaceFavorites.map((favorite) => favorite.recordId),

View File

@@ -4,7 +4,6 @@ import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigat
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection'; import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection'; import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
@@ -78,19 +77,15 @@ export const NavigationDrawerSectionForObjectMetadataItems = ({
onClick={() => toggleNavigationSection()} onClick={() => toggleNavigationSection()}
/> />
</NavigationDrawerAnimatedCollapseWrapper> </NavigationDrawerAnimatedCollapseWrapper>
<ScrollWrapper contextProviderName="navigationDrawer"> <StyledObjectsMetaDataItemsWrapper>
<StyledObjectsMetaDataItemsWrapper> {isNavigationSectionOpen &&
{isNavigationSectionOpen && objectMetadataItemsForNavigationItems.map((objectMetadataItem) => (
objectMetadataItemsForNavigationItems.map( <NavigationDrawerItemForObjectMetadataItem
(objectMetadataItem) => ( key={`navigation-drawer-item-${objectMetadataItem.id}`}
<NavigationDrawerItemForObjectMetadataItem objectMetadataItem={objectMetadataItem}
key={`navigation-drawer-item-${objectMetadataItem.id}`} />
objectMetadataItem={objectMetadataItem} ))}
/> </StyledObjectsMetaDataItemsWrapper>
),
)}
</StyledObjectsMetaDataItemsWrapper>
</ScrollWrapper>
</NavigationDrawerSection> </NavigationDrawerSection>
) )
); );

View File

@@ -7,16 +7,12 @@ import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/o
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading'; import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
export const NavigationDrawerSectionForObjectMetadataItemsWrapper = ({ export const RemoteNavigationDrawerSection = () => {
isRemote,
}: {
isRemote: boolean;
}) => {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
const filteredActiveObjectMetadataItems = activeObjectMetadataItems.filter( const filteredActiveObjectMetadataItems = activeObjectMetadataItems.filter(
(item) => (isRemote ? item.isRemote : !item.isRemote), (item) => item.isRemote,
); );
const loading = useIsPrefetchLoading(); const loading = useIsPrefetchLoading();
@@ -26,9 +22,9 @@ export const NavigationDrawerSectionForObjectMetadataItemsWrapper = ({
return ( return (
<NavigationDrawerSectionForObjectMetadataItems <NavigationDrawerSectionForObjectMetadataItems
sectionTitle={isRemote ? 'Remote' : 'Workspace'} sectionTitle={'Remote'}
objectMetadataItems={filteredActiveObjectMetadataItems} objectMetadataItems={filteredActiveObjectMetadataItems}
isRemote={isRemote} isRemote={true}
/> />
); );
}; };

View File

@@ -1,44 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { NavigationDrawerSectionForObjectMetadataItemsWrapper } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsWrapper';
import { within } from '@storybook/test';
import { PrefetchLoadedDecorator } from '~/testing/decorators/PrefetchLoadedDecorator';
const meta: Meta<typeof NavigationDrawerSectionForObjectMetadataItemsWrapper> =
{
title:
'Modules/ObjectMetadata/NavigationDrawerSectionForObjectMetadataItemsWrapper',
component: NavigationDrawerSectionForObjectMetadataItemsWrapper,
decorators: [
IconsProviderDecorator,
ObjectMetadataItemsDecorator,
ComponentWithRouterDecorator,
ComponentWithRecoilScopeDecorator,
SnackBarDecorator,
PrefetchLoadedDecorator,
],
parameters: {
msw: graphqlMocks,
},
};
export default meta;
type Story = StoryObj<
typeof NavigationDrawerSectionForObjectMetadataItemsWrapper
>;
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('People', undefined, { timeout: 10000 });
await canvas.findByText('Companies');
await canvas.findByText('Opportunities');
},
};

View File

@@ -1,3 +1,4 @@
import { PageFavoriteFoldersDropdown } from '@/favorites/components/PageFavoriteFolderDropdown';
import { FAVORITE_FOLDER_PICKER_DROPDOWN_ID } from '@/favorites/favorite-folder-picker/constants/FavoriteFolderPickerDropdownId'; import { FAVORITE_FOLDER_PICKER_DROPDOWN_ID } from '@/favorites/favorite-folder-picker/constants/FavoriteFolderPickerDropdownId';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
@@ -8,7 +9,6 @@ import { recordIndexViewTypeState } from '@/object-record/record-index/states/re
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { PageAddButton } from '@/ui/layout/page/components/PageAddButton'; import { PageAddButton } from '@/ui/layout/page/components/PageAddButton';
import { PageFavoriteFoldersDropdown } from '@/ui/layout/page/components/PageFavoriteFolderDropdown';
import { PageHeader } from '@/ui/layout/page/components/PageHeader'; import { PageHeader } from '@/ui/layout/page/components/PageHeader';
import { PageHotkeysEffect } from '@/ui/layout/page/components/PageHotkeysEffect'; import { PageHotkeysEffect } from '@/ui/layout/page/components/PageHotkeysEffect';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@@ -39,7 +39,7 @@ export const RecordIndexPageHeader = () => {
const view = views.find((view) => view.id === currentViewId); const view = views.find((view) => view.id === currentViewId);
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const isFavorite = favorites.some( const isFavorite = favorites.some(
(favorite) => (favorite) =>

View File

@@ -36,9 +36,9 @@ export const useRecordShowPage = (
const { objectMetadataItems } = useObjectMetadataItems(); const { objectMetadataItems } = useObjectMetadataItems();
const { labelIdentifierFieldMetadataItem } = const { labelIdentifierFieldMetadataItem } =
useLabelIdentifierFieldMetadataItem({ objectNameSingular }); useLabelIdentifierFieldMetadataItem({ objectNameSingular });
const favorites = useFavorites(); const { sortedFavorites: favorites } = useFavorites();
const createFavorite = useCreateFavorite(); const { createFavorite } = useCreateFavorite();
const deleteFavorite = useDeleteFavorite(); const { deleteFavorite } = useDeleteFavorite();
const setEntityFields = useSetRecoilState( const setEntityFields = useSetRecoilState(
recordStoreFamilyState(objectRecordId), recordStoreFamilyState(objectRecordId),
); );

View File

@@ -9,6 +9,7 @@ import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
// TODO: Remove this component once we merge it with PrefetchRunQueriesEffect (once we remove feature flag)
export const PrefetchFavoriteFoldersRunQueriesEffect = () => { export const PrefetchFavoriteFoldersRunQueriesEffect = () => {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
@@ -19,7 +20,6 @@ export const PrefetchFavoriteFoldersRunQueriesEffect = () => {
const { objectMetadataItems } = useObjectMetadataItems(); const { objectMetadataItems } = useObjectMetadataItems();
// Only include favorite folders operation
const operationSignatures = Object.values(PREFETCH_CONFIG) const operationSignatures = Object.values(PREFETCH_CONFIG)
.filter(({ objectNameSingular }) => objectNameSingular === 'favoriteFolder') .filter(({ objectNameSingular }) => objectNameSingular === 'favoriteFolder')
.map(({ objectNameSingular, operationSignatureFactory }) => { .map(({ objectNameSingular, operationSignatureFactory }) => {

View File

@@ -9,14 +9,10 @@ import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery'; import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
export const PrefetchRunQueriesEffect = () => { export const PrefetchRunQueriesEffect = () => {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
const isFavoriteFolderEnabled = useIsFeatureEnabled(
'IS_FAVORITE_FOLDER_ENABLED',
);
const { upsertRecordsInCache: upsertViewsInCache } = const { upsertRecordsInCache: upsertViewsInCache } =
usePrefetchRunQuery<View>({ usePrefetchRunQuery<View>({
@@ -33,7 +29,7 @@ export const PrefetchRunQueriesEffect = () => {
const operationSignatures = Object.values(PREFETCH_CONFIG) const operationSignatures = Object.values(PREFETCH_CONFIG)
.filter( .filter(
({ objectNameSingular }) => ({ objectNameSingular }) =>
// Exclude favorite folders as they're handled separately // TODO: Remove this filter once we merge PrefetchFavortiteFoldersRunQueriesEffect with this component
objectNameSingular !== 'favoriteFolder', objectNameSingular !== 'favoriteFolder',
) )
.map(({ objectNameSingular, operationSignatureFactory }) => { .map(({ objectNameSingular, operationSignatureFactory }) => {
@@ -57,12 +53,7 @@ export const PrefetchRunQueriesEffect = () => {
if (isDefined(result.favorites)) { if (isDefined(result.favorites)) {
upsertFavoritesInCache(result.favorites as Favorite[]); upsertFavoritesInCache(result.favorites as Favorite[]);
} }
}, [ }, [result, upsertViewsInCache, upsertFavoritesInCache]);
result,
upsertViewsInCache,
upsertFavoritesInCache,
isFavoriteFolderEnabled,
]);
return <></>; return <></>;
}; };

View File

@@ -1,11 +0,0 @@
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { useRecoilValue } from 'recoil';
export const useIsFavoriteFoldersPrefetchLoading = () => {
const areFavoritesFolderPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
);
return !areFavoritesFolderPrefetched;
};

View File

@@ -2,13 +2,14 @@ import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedF
import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useIsFavoriteFoldersPrefetchLoading } from './useIsFavoriteFoldersPrefetchLoading';
export const useIsPrefetchLoading = () => { export const useIsPrefetchLoading = () => {
const isFavoriteFolderEnabled = useIsFeatureEnabled( const isFavoriteFolderEnabled = useIsFeatureEnabled(
'IS_FAVORITE_FOLDER_ENABLED', 'IS_FAVORITE_FOLDER_ENABLED',
); );
const isFavoriteFoldersLoading = useIsFavoriteFoldersPrefetchLoading(); const isFavoriteFoldersPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
);
const areViewsPrefetched = useRecoilValue( const areViewsPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllViews), prefetchIsLoadedFamilyState(PrefetchKey.AllViews),
@@ -20,6 +21,6 @@ export const useIsPrefetchLoading = () => {
return ( return (
!areViewsPrefetched || !areViewsPrefetched ||
!areFavoritesPrefetched || !areFavoritesPrefetched ||
(isFavoriteFolderEnabled && isFavoriteFoldersLoading) (isFavoriteFolderEnabled && !isFavoriteFoldersPrefetched)
); );
}; };

View File

@@ -1,10 +1,10 @@
import { useState } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { import {
DragDropContext, DragDropContext,
Droppable, Droppable,
OnDragEndResponder, OnDragEndResponder,
} from '@hello-pangea/dnd'; } from '@hello-pangea/dnd';
import { useState } from 'react';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
type DraggableListProps = { type DraggableListProps = {
draggableItems: React.ReactNode; draggableItems: React.ReactNode;

View File

@@ -23,12 +23,17 @@ const StyledScrollWrapper = styled.div`
} }
`; `;
const StyledInnerContainer = styled.div`
flex: 1;
`;
export type ScrollWrapperProps = { export type ScrollWrapperProps = {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
enableXScroll?: boolean; enableXScroll?: boolean;
enableYScroll?: boolean; enableYScroll?: boolean;
contextProviderName: ContextProviderName; contextProviderName: ContextProviderName;
scrollHide?: boolean;
}; };
export const ScrollWrapper = ({ export const ScrollWrapper = ({
@@ -37,6 +42,7 @@ export const ScrollWrapper = ({
enableXScroll = true, enableXScroll = true,
enableYScroll = true, enableYScroll = true,
contextProviderName, contextProviderName,
scrollHide = false,
}: ScrollWrapperProps) => { }: ScrollWrapperProps) => {
const scrollableRef = useRef<HTMLDivElement>(null); const scrollableRef = useRef<HTMLDivElement>(null);
const Context = getContextByProviderName(contextProviderName); const Context = getContextByProviderName(contextProviderName);
@@ -55,7 +61,10 @@ export const ScrollWrapper = ({
const [initialize, instance] = useOverlayScrollbars({ const [initialize, instance] = useOverlayScrollbars({
options: { options: {
scrollbars: { autoHide: 'scroll' }, scrollbars: {
autoHide: scrollHide ? 'scroll' : 'never',
visibility: scrollHide ? 'hidden' : 'visible',
},
overflow: { overflow: {
x: enableXScroll ? undefined : 'hidden', x: enableXScroll ? undefined : 'hidden',
y: enableYScroll ? undefined : 'hidden', y: enableYScroll ? undefined : 'hidden',
@@ -84,7 +93,7 @@ export const ScrollWrapper = ({
}} }}
> >
<StyledScrollWrapper ref={scrollableRef} className={className}> <StyledScrollWrapper ref={scrollableRef} className={className}>
{children} <StyledInnerContainer>{children}</StyledInnerContainer>
</StyledScrollWrapper> </StyledScrollWrapper>
</Context.Provider> </Context.Provider>
); );

View File

@@ -1,8 +1,8 @@
import { PageFavoriteButton } from '@/favorites/components/PageFavoriteButton';
import { PageFavoriteFoldersDropdown } from '@/favorites/components/PageFavoriteFolderDropdown';
import { FAVORITE_FOLDER_PICKER_DROPDOWN_ID } from '@/favorites/favorite-folder-picker/constants/FavoriteFolderPickerDropdownId'; import { FAVORITE_FOLDER_PICKER_DROPDOWN_ID } from '@/favorites/favorite-folder-picker/constants/FavoriteFolderPickerDropdownId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { PageFavoriteButton } from '@/ui/layout/page/components/PageFavoriteButton';
import { PageFavoriteFoldersDropdown } from '@/ui/layout/page/components/PageFavoriteFolderDropdown';
import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddButton'; import { ShowPageAddButton } from '@/ui/layout/show-page/components/ShowPageAddButton';
import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMoreButton'; import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMoreButton';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';