diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenu.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenu.tsx
index b4c7e1bf357c..a4961ccf78d2 100644
--- a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenu.tsx
+++ b/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenu.tsx
@@ -14,11 +14,13 @@ export const RecordShowActionMenu = ({
record,
objectMetadataItem,
objectNameSingular,
+ handleFavoriteButtonClick,
}: {
isFavorite: boolean;
record: ObjectRecord | undefined;
objectMetadataItem: ObjectMetadataItem;
objectNameSingular: string;
+ handleFavoriteButtonClick: () => void;
}) => {
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
@@ -41,6 +43,7 @@ export const RecordShowActionMenu = ({
record,
objectMetadataItem,
objectNameSingular,
+ handleFavoriteButtonClick,
}}
/>
diff --git a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx
index 70019c3ce175..a32cb4ebc755 100644
--- a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx
+++ b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx
@@ -44,7 +44,7 @@ export const CurrentWorkspaceMemberFavorites = ({
onToggle,
}: CurrentWorkspaceMemberFavoritesProps) => {
const currentPath = useLocation().pathname;
- const currentPathView = useLocation().pathname + useLocation().search;
+ const currentViewPath = useLocation().pathname + useLocation().search;
const theme = useTheme();
const [isRenaming, setIsRenaming] = useState(false);
@@ -57,7 +57,7 @@ export const CurrentWorkspaceMemberFavorites = ({
);
const selectedFavoriteIndex = folder.favorites.findIndex((favorite) =>
favorite.objectNameSingular === 'view'
- ? favorite.link === currentPathView
+ ? favorite.link === currentViewPath
: favorite.link === currentPath,
);
const { deleteFavorite, handleReorderFavorite } = useFavorites();
@@ -189,7 +189,7 @@ export const CurrentWorkspaceMemberFavorites = ({
to={favorite.link}
active={
favorite.objectNameSingular === 'view'
- ? favorite.link === currentPathView
+ ? favorite.link === currentViewPath
: favorite.link === currentPath
}
subItemState={getNavigationSubItemState({
diff --git a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavoritesFolder.tsx b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavoritesFolder.tsx
index 1d0b9f7e86b1..a3b8e6fc5429 100644
--- a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavoritesFolder.tsx
+++ b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavoritesFolder.tsx
@@ -1,26 +1,24 @@
+import { useTheme } from '@emotion/react';
+import styled from '@emotion/styled';
+import { useLocation } from 'react-router-dom';
+import { useRecoilState, useRecoilValue } from 'recoil';
+import { IconFolderPlus, IconHeartOff, isDefined } from 'twenty-ui';
+
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
-import { CurrentWorkspaceMemberFavorites } from '@/favorites/components/CurrentWorkspaceMemberFavorites';
import { FavoriteIcon } from '@/favorites/components/FavoriteIcon';
+import { FavoriteFolders } from '@/favorites/components/FavoritesFolders';
import { FavoritesSkeletonLoader } from '@/favorites/components/FavoritesSkeletonLoader';
-import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
-import { useFavoriteFolders } from '@/favorites/hooks/useFavoriteFolders';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { isFavoriteFolderCreatingState } from '@/favorites/states/isFavoriteFolderCreatingState';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
-import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
-import { useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-import { useState } from 'react';
-import { useLocation } from 'react-router-dom';
-import { useRecoilState, useRecoilValue } from 'recoil';
-import { IconFolder, IconFolderPlus, IconHeartOff, isDefined } from 'twenty-ui';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
const StyledContainer = styled(NavigationDrawerSection)`
width: 100%;
@@ -37,58 +35,25 @@ const StyledNavigationDrawerItem = styled(NavigationDrawerItem)`
`;
export const CurrentWorkspaceMemberFavoritesFolders = () => {
- const [activeFolderId, setActiveFolderId] = useState(null);
- const { createFolder, favoriteFolder } = useFavoriteFolders();
- const { deleteFavorite } = useFavorites();
- const [folderName, setFolderName] = useState('');
const currentPath = useLocation().pathname;
- const currentPathView = useLocation().pathname + useLocation().search;
- const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
- useRecoilState(isFavoriteFolderCreatingState);
+ const currentViewPath = useLocation().pathname + useLocation().search;
const theme = useTheme();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
- const { favorites, handleReorderFavorite, favoritesByFolder } =
- useFavorites();
+ const { favorites, handleReorderFavorite, deleteFavorite } = useFavorites();
+ const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
+ useRecoilState(isFavoriteFolderCreatingState);
+
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
const loading = useIsPrefetchLoading();
+
const { toggleNavigationSection, isNavigationSectionOpenState } =
useNavigationSection('Favorites');
const isNavigationSectionOpen = useRecoilValue(isNavigationSectionOpenState);
- const handleFolderNameChange = (value: string) => {
- setFolderName(value);
- };
-
- const handleSubmitFolder = async (value: string) => {
- if (!value) return false;
-
- setIsFavoriteFolderCreating(false);
- setFolderName('');
- await createFolder(value);
- return true;
- };
-
- const handleClickOutside = async (
- event: MouseEvent | TouchEvent,
- value: string,
- ) => {
- if (!value) {
- setIsFavoriteFolderCreating(false);
- return;
- }
-
- setIsFavoriteFolderCreating(false);
- setFolderName('');
- await createFolder(value);
- };
-
- const handleCancel = () => {
- setFolderName('');
- setIsFavoriteFolderCreating(false);
- };
-
const toggleNewFolder = () => {
setIsFavoriteFolderCreating((current) => !current);
- setFolderName('');
};
if (loading && isDefined(currentWorkspaceMember)) {
@@ -99,59 +64,42 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
(favorite) => favorite.workspaceMemberId === currentWorkspaceMember?.id,
);
+ const unorganisedFavorites = currentWorkspaceMemberFavorites.filter(
+ (favorite) => !favorite.favoriteFolderId,
+ );
+
if (
(!currentWorkspaceMemberFavorites ||
currentWorkspaceMemberFavorites.length === 0) &&
- (!favoriteFolder || favoriteFolder.length === 0) &&
!isFavoriteFolderCreating
) {
- return <>>;
+ return null;
}
- const unorganisedFavorites = currentWorkspaceMemberFavorites.filter(
- (favorite) => !favorite.favoriteFolderId,
- );
- const isGroup =
- currentWorkspaceMemberFavorites.length > 1 || favoritesByFolder.length > 1;
-
return (
toggleNavigationSection()}
- rightIcon={}
- onRightIconClick={toggleNewFolder}
+ onClick={toggleNavigationSection}
+ rightIcon={
+ isFavoriteFolderEnabled ? (
+
+ ) : undefined
+ }
+ onRightIconClick={
+ isFavoriteFolderEnabled ? toggleNewFolder : undefined
+ }
/>
+
{isNavigationSectionOpen && (
<>
- {isFavoriteFolderCreating && (
-
)}
- {favoritesByFolder.map((folder) => (
- {
- setActiveFolderId((currentId) =>
- currentId === folderId ? null : folderId,
- );
- }}
- />
- ))}
{unorganisedFavorites.length > 0 && (
{
Icon={() => }
active={
favorite.objectNameSingular === 'view'
- ? favorite.link === currentPathView
+ ? favorite.link === currentViewPath
: favorite.link === currentPath
}
to={favorite.link}
diff --git a/packages/twenty-front/src/modules/favorites/components/FavoritesFolders.tsx b/packages/twenty-front/src/modules/favorites/components/FavoritesFolders.tsx
new file mode 100644
index 000000000000..a385ebe55c55
--- /dev/null
+++ b/packages/twenty-front/src/modules/favorites/components/FavoritesFolders.tsx
@@ -0,0 +1,89 @@
+import { useState } from 'react';
+import { useRecoilState } from 'recoil';
+import { IconFolder } from 'twenty-ui';
+
+import { CurrentWorkspaceMemberFavorites } from '@/favorites/components/CurrentWorkspaceMemberFavorites';
+import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
+import { useFavoriteFolders } from '@/favorites/hooks/useFavoriteFolders';
+import { isFavoriteFolderCreatingState } from '@/favorites/states/isFavoriteFolderCreatingState';
+import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
+
+type FavoriteFoldersProps = {
+ isNavigationSectionOpen: boolean;
+};
+
+export const FavoriteFolders = ({
+ isNavigationSectionOpen,
+}: FavoriteFoldersProps) => {
+ const [activeFolderId, setActiveFolderId] = useState(null);
+ const [folderName, setFolderName] = useState('');
+
+ const { createFolder, favoritesByFolder } = useFavoriteFolders();
+ const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
+ useRecoilState(isFavoriteFolderCreatingState);
+
+ const handleFolderNameChange = (value: string) => {
+ setFolderName(value);
+ };
+
+ const handleSubmitFolder = async (value: string) => {
+ if (!value) return false;
+
+ setIsFavoriteFolderCreating(false);
+ setFolderName('');
+ await createFolder(value);
+ return true;
+ };
+
+ const handleClickOutside = async (
+ event: MouseEvent | TouchEvent,
+ value: string,
+ ) => {
+ if (!value) {
+ setIsFavoriteFolderCreating(false);
+ return;
+ }
+
+ setIsFavoriteFolderCreating(false);
+ setFolderName('');
+ await createFolder(value);
+ };
+
+ const handleCancel = () => {
+ setFolderName('');
+ setIsFavoriteFolderCreating(false);
+ };
+
+ if (!isNavigationSectionOpen) {
+ return null;
+ }
+
+ return (
+ <>
+ {isFavoriteFolderCreating && (
+
+ )}
+ {favoritesByFolder.map((folder) => (
+ 1}
+ isOpen={activeFolderId === folder.folderId}
+ onToggle={(folderId) => {
+ setActiveFolderId((currentId) =>
+ currentId === folderId ? null : folderId,
+ );
+ }}
+ />
+ ))}
+ >
+ );
+};
diff --git a/packages/twenty-front/src/modules/favorites/hooks/useFavoriteFolders.ts b/packages/twenty-front/src/modules/favorites/hooks/useFavoriteFolders.ts
index 9e80c02d1c6b..85db0a82ccbf 100644
--- a/packages/twenty-front/src/modules/favorites/hooks/useFavoriteFolders.ts
+++ b/packages/twenty-front/src/modules/favorites/hooks/useFavoriteFolders.ts
@@ -1,14 +1,33 @@
+import { usePrefetchedFavoritesFoldersData } from '@/favorites/hooks/usePrefetchedfavoritesFoldersData';
import { calculateNewPosition } from '@/favorites/utils/calculateNewPosition';
+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 { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
+import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
+import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
+import { View } from '@/views/types/View';
import { OnDragEndResponder } from '@hello-pangea/dnd';
+import { useMemo } from 'react';
+import { useRecoilValue } from 'recoil';
+import { FieldMetadataType } from '~/generated-metadata/graphql';
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
export const useFavoriteFolders = () => {
- const { folders, favorites, upsertFavorites, currentWorkspaceMemberId } =
+ const { favorites, upsertFavorites, currentWorkspaceMemberId } =
usePrefetchedFavoritesData();
+ const { records: views } = usePrefetchedData(PrefetchKey.AllViews);
+ const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
+
+ const { folders } = usePrefetchedFavoritesFoldersData();
+ const { objectMetadataItem: favoriteObjectMetadataItem } =
+ useObjectMetadataItem({
+ objectNameSingular: CoreObjectNameSingular.Favorite,
+ });
const { deleteOneRecord } = useDeleteOneRecord({
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
@@ -21,6 +40,16 @@ export const useFavoriteFolders = () => {
const { createOneRecord: createFavoriteFolder } = useCreateOneRecord({
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
});
+ const favoriteRelationFieldMetadataItems = useMemo(
+ () =>
+ favoriteObjectMetadataItem.fields.filter(
+ (fieldMetadataItem) =>
+ fieldMetadataItem.type === FieldMetadataType.Relation &&
+ fieldMetadataItem.name !== 'workspaceMember' &&
+ fieldMetadataItem.name !== 'favoriteFolder',
+ ),
+ [favoriteObjectMetadataItem.fields],
+ );
const createFolder = async (name: string): Promise => {
if (!name || !currentWorkspaceMemberId) {
@@ -85,9 +114,32 @@ export const useFavoriteFolders = () => {
);
upsertFavorites(updatedFavorites);
};
-
+ const getObjectRecordIdentifierByNameSingular =
+ useGetObjectRecordIdentifierByNameSingular();
+ const favoritesByFolder = useMemo(() => {
+ return folders.map((folder) => ({
+ folderId: folder.id,
+ folderName: folder.name,
+ favorites: sortFavorites(
+ favorites.filter((favorite) => favorite.favoriteFolderId === folder.id),
+ favoriteRelationFieldMetadataItems,
+ getObjectRecordIdentifierByNameSingular,
+ true,
+ views,
+ objectMetadataItems,
+ ),
+ }));
+ }, [
+ folders,
+ favorites,
+ favoriteRelationFieldMetadataItems,
+ getObjectRecordIdentifierByNameSingular,
+ views,
+ objectMetadataItems,
+ ]);
return {
favoriteFolder: folders,
+ favoritesByFolder,
createFolder,
renameFolder,
deleteFolder,
diff --git a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
index 33d2a41537f7..5c4ead57b5b0 100644
--- a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
+++ b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
@@ -18,7 +18,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData';
export const useFavorites = () => {
- const { favorites, workspaceFavorites, folders, currentWorkspaceMemberId } =
+ const { favorites, workspaceFavorites, currentWorkspaceMemberId } =
usePrefetchedFavoritesData();
const { records: views } = usePrefetchedData(PrefetchKey.AllViews);
@@ -89,28 +89,6 @@ export const useFavorites = () => {
objectMetadataItems,
]);
- const favoritesByFolder = useMemo(() => {
- return folders.map((folder) => ({
- folderId: folder.id,
- folderName: folder.name,
- favorites: sortFavorites(
- favorites.filter((favorite) => favorite.favoriteFolderId === folder.id),
- favoriteRelationFieldMetadataItems,
- getObjectRecordIdentifierByNameSingular,
- true,
- views,
- objectMetadataItems,
- ),
- }));
- }, [
- folders,
- favorites,
- favoriteRelationFieldMetadataItems,
- getObjectRecordIdentifierByNameSingular,
- views,
- objectMetadataItems,
- ]);
-
const createFavorite = (
targetRecord: ObjectRecord,
targetObjectNameSingular: string,
@@ -168,7 +146,6 @@ export const useFavorites = () => {
return {
favorites: favoritesSorted,
workspaceFavorites: workspaceFavoritesSorted,
- favoritesByFolder,
createFavorite,
handleReorderFavorite,
deleteFavorite,
diff --git a/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesData.ts b/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesData.ts
index 553dbdedd0e5..1c851d60508e 100644
--- a/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesData.ts
+++ b/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesData.ts
@@ -1,27 +1,19 @@
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Favorite } from '@/favorites/types/Favorite';
-import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
-import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
type PrefetchedFavoritesData = {
favorites: Favorite[];
workspaceFavorites: Favorite[];
- folders: FavoriteFolder[];
upsertFavorites: (records: Favorite[]) => void;
- upsertFolders: (records: FavoriteFolder[]) => void;
currentWorkspaceMemberId: string | undefined;
};
export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
- const isFavoriteFolderEnabled = useIsFeatureEnabled(
- 'IS_FAVORITE_FOLDER_ENABLED',
- );
-
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const currentWorkspaceMemberId = currentWorkspaceMember?.id;
const { records: _favorites } = usePrefetchedData(
@@ -33,15 +25,6 @@ export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
},
);
- const { records: _folders } = usePrefetchedData(
- PrefetchKey.AllFavoritesFolders,
- {
- workspaceMemberId: {
- eq: currentWorkspaceMemberId,
- },
- },
- );
-
const favorites = useMemo(
() =>
_favorites.filter(
@@ -53,34 +36,19 @@ export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
() => _favorites.filter((favorite) => favorite.workspaceMemberId === null),
[_favorites],
);
- const folders = useMemo(() => _folders, [_folders]);
const { upsertRecordsInCache: upsertFavorites } =
usePrefetchRunQuery({
prefetchKey: PrefetchKey.AllFavorites,
});
- const { upsertRecordsInCache: upsertFolders } =
- usePrefetchRunQuery({
- prefetchKey: PrefetchKey.AllFavoritesFolders,
- });
-
return useMemo(
() => ({
favorites,
workspaceFavorites,
- folders,
upsertFavorites,
- upsertFolders,
currentWorkspaceMemberId,
}),
- [
- favorites,
- workspaceFavorites,
- folders,
- upsertFavorites,
- upsertFolders,
- currentWorkspaceMemberId,
- ],
+ [favorites, workspaceFavorites, upsertFavorites, currentWorkspaceMemberId],
);
};
diff --git a/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesFoldersData.ts b/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesFoldersData.ts
new file mode 100644
index 000000000000..40316b78162c
--- /dev/null
+++ b/packages/twenty-front/src/modules/favorites/hooks/usePrefetchedFavoritesFoldersData.ts
@@ -0,0 +1,44 @@
+import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
+import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
+import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
+import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
+import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
+import { useMemo } from 'react';
+import { useRecoilValue } from 'recoil';
+
+type PrefetchedFavoritesFoldersData = {
+ folders: FavoriteFolder[];
+ upsertFolders: (records: FavoriteFolder[]) => void;
+ currentWorkspaceMemberId: string | undefined;
+};
+
+export const usePrefetchedFavoritesFoldersData =
+ (): PrefetchedFavoritesFoldersData => {
+ const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
+ const currentWorkspaceMemberId = currentWorkspaceMember?.id;
+
+ const { records: _folders } = usePrefetchedData(
+ PrefetchKey.AllFavoritesFolders,
+ {
+ workspaceMemberId: {
+ eq: currentWorkspaceMemberId,
+ },
+ },
+ );
+
+ const folders = useMemo(() => _folders, [_folders]);
+
+ const { upsertRecordsInCache: upsertFolders } =
+ usePrefetchRunQuery({
+ prefetchKey: PrefetchKey.AllFavoritesFolders,
+ });
+
+ return useMemo(
+ () => ({
+ folders,
+ upsertFolders,
+ currentWorkspaceMemberId,
+ }),
+ [folders, upsertFolders, currentWorkspaceMemberId],
+ );
+ };
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
index 9c1ac1653ba4..90c862c01bfa 100644
--- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerItems.tsx
@@ -35,6 +35,7 @@ export const MainNavigationDrawerItems = () => {
const isWorkspaceFavoriteEnabled = useIsFeatureEnabled(
'IS_WORKSPACE_FAVORITE_ENABLED',
);
+
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useRecoilState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetRecoilState(
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx
index 91d934b3b1fd..5a8314835e93 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx
@@ -1,5 +1,5 @@
import { useRecoilValue } from 'recoil';
-import { useIcons } from 'twenty-ui';
+import { isDefined, useIcons } from 'twenty-ui';
import { FAVORITE_FOLDERS_DROPDOWN_ID } from '@/favorites/constants/FavoriteFoldersDropdownId';
import { useFavorites } from '@/favorites/hooks/useFavorites';
@@ -11,6 +11,7 @@ import { recordIndexViewTypeState } from '@/object-record/record-index/states/re
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { PageAddButton } from '@/ui/layout/page/components/PageAddButton';
+import { PageFavoriteButton } from '@/ui/layout/page/components/PageFavoriteButton';
import { PageFavoriteFoldersDropdown } from '@/ui/layout/page/components/PageFavoriteFolderDropdown';
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
import { PageHotkeysEffect } from '@/ui/layout/page/components/PageHotkeysEffect';
@@ -18,12 +19,16 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { View } from '@/views/types/View';
import { ViewType } from '@/views/types/ViewType';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useContext } from 'react';
import { capitalize } from '~/utils/string/capitalize';
export const RecordIndexPageHeader = () => {
const { findObjectMetadataItemByNamePlural } =
useFilteredObjectMetadataItems();
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
const { objectNamePlural, onCreateRecord, recordIndexId } = useContext(
RecordIndexRootPropsContext,
@@ -36,12 +41,27 @@ export const RecordIndexPageHeader = () => {
const view = views.find((view) => view.id === currentViewId);
- const { favorites } = useFavorites();
+ const { favorites, createFavorite, deleteFavorite } = useFavorites();
const isFavorite = favorites.some(
(favorite) =>
favorite.recordId === currentViewId && favorite.workspaceMemberId,
);
+
+ const handleFavoriteButtonClick = async () => {
+ if (!view) return;
+
+ const correspondingFavorite = favorites.find(
+ (favorite) =>
+ favorite.recordId === currentViewId && favorite.workspaceMemberId,
+ );
+
+ if (isFavorite && isDefined(correspondingFavorite)) {
+ deleteFavorite(correspondingFavorite.id);
+ } else {
+ createFavorite(view, 'view');
+ }
+ };
const objectMetadataItem =
findObjectMetadataItemByNamePlural(objectNamePlural);
@@ -68,12 +88,19 @@ export const RecordIndexPageHeader = () => {
return (
-
+ {isFavoriteFolderEnabled ? (
+
+ ) : (
+
+ )}
{shouldDisplayAddButton &&
(isTable ? (
diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts
index 7e779d443591..ad76ba9d2087 100644
--- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts
+++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts
@@ -34,7 +34,7 @@ export const useRecordShowPage = (
const { objectMetadataItems } = useObjectMetadataItems();
const { labelIdentifierFieldMetadataItem } =
useLabelIdentifierFieldMetadataItem({ objectNameSingular });
- const { favorites } = useFavorites();
+ const { favorites, createFavorite, deleteFavorite } = useFavorites();
const setEntityFields = useSetRecoilState(
recordStoreFamilyState(objectRecordId),
);
@@ -64,6 +64,16 @@ export const useRecordShowPage = (
);
const isFavorite = isDefined(correspondingFavorite);
+ const handleFavoriteButtonClick = async () => {
+ if (!objectNameSingular || !record) return;
+
+ if (isFavorite) {
+ deleteFavorite(correspondingFavorite.id);
+ } else {
+ createFavorite(record, objectNameSingular);
+ }
+ };
+
const labelIdentifierFieldValue =
record?.[labelIdentifierFieldMetadataItem?.name ?? ''];
const pageName =
@@ -90,5 +100,6 @@ export const useRecordShowPage = (
isFavorite,
record,
objectMetadataItem,
+ handleFavoriteButtonClick,
};
};
diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchDataProvider.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchDataProvider.tsx
index 837d2498e576..87f5228daf72 100644
--- a/packages/twenty-front/src/modules/prefetch/components/PrefetchDataProvider.tsx
+++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchDataProvider.tsx
@@ -1,11 +1,17 @@
import React from 'react';
+import { PrefetchFavoriteFoldersRunQueriesEffect } from '@/prefetch/components/PrefetchFavortiteFoldersRunQueriesEffect';
import { PrefetchRunQueriesEffect } from '@/prefetch/components/PrefetchRunQueriesEffect';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
export const PrefetchDataProvider = ({ children }: React.PropsWithChildren) => {
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
return (
<>
+ {isFavoriteFolderEnabled && }
{children}
>
);
diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchFavortiteFoldersRunQueriesEffect.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchFavortiteFoldersRunQueriesEffect.tsx
new file mode 100644
index 000000000000..376c1e46942b
--- /dev/null
+++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchFavortiteFoldersRunQueriesEffect.tsx
@@ -0,0 +1,45 @@
+import { currentUserState } from '@/auth/states/currentUserState';
+import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
+import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
+import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords';
+import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
+import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
+import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
+import { useEffect } from 'react';
+import { useRecoilValue } from 'recoil';
+import { isDefined } from '~/utils/isDefined';
+
+export const PrefetchFavoriteFoldersRunQueriesEffect = () => {
+ const currentUser = useRecoilValue(currentUserState);
+
+ const { upsertRecordsInCache: upsertFavoritesFoldersInCache } =
+ usePrefetchRunQuery({
+ prefetchKey: PrefetchKey.AllFavoritesFolders,
+ });
+
+ const { objectMetadataItems } = useObjectMetadataItems();
+
+ // Only include favorite folders operation
+ const operationSignatures = Object.values(PREFETCH_CONFIG)
+ .filter(({ objectNameSingular }) => objectNameSingular === 'favoriteFolder')
+ .map(({ objectNameSingular, operationSignatureFactory }) => {
+ const objectMetadataItem = objectMetadataItems.find(
+ (item) => item.nameSingular === objectNameSingular,
+ );
+
+ return operationSignatureFactory({ objectMetadataItem });
+ });
+
+ const { result } = useCombinedFindManyRecords({
+ operationSignatures,
+ skip: !currentUser,
+ });
+
+ useEffect(() => {
+ if (isDefined(result.favoriteFolders)) {
+ upsertFavoritesFoldersInCache(result.favoriteFolders as FavoriteFolder[]);
+ }
+ }, [result, upsertFavoritesFoldersInCache]);
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
index 17106a10f83b..bad5c0b5aead 100644
--- a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
+++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
@@ -3,17 +3,20 @@ import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { Favorite } from '@/favorites/types/Favorite';
-import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords';
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isDefined } from '~/utils/isDefined';
export const PrefetchRunQueriesEffect = () => {
const currentUser = useRecoilValue(currentUserState);
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
const { upsertRecordsInCache: upsertViewsInCache } =
usePrefetchRunQuery({
@@ -25,22 +28,21 @@ export const PrefetchRunQueriesEffect = () => {
prefetchKey: PrefetchKey.AllFavorites,
});
- const { upsertRecordsInCache: upsertFavoritesFoldersInCache } =
- usePrefetchRunQuery({
- prefetchKey: PrefetchKey.AllFavoritesFolders,
- });
-
const { objectMetadataItems } = useObjectMetadataItems();
- const operationSignatures = Object.values(PREFETCH_CONFIG).map(
- ({ objectNameSingular, operationSignatureFactory }) => {
+ const operationSignatures = Object.values(PREFETCH_CONFIG)
+ .filter(
+ ({ objectNameSingular }) =>
+ // Exclude favorite folders as they're handled separately
+ objectNameSingular !== 'favoriteFolder',
+ )
+ .map(({ objectNameSingular, operationSignatureFactory }) => {
const objectMetadataItem = objectMetadataItems.find(
(item) => item.nameSingular === objectNameSingular,
);
return operationSignatureFactory({ objectMetadataItem });
- },
- );
+ });
const { result } = useCombinedFindManyRecords({
operationSignatures,
@@ -55,15 +57,11 @@ export const PrefetchRunQueriesEffect = () => {
if (isDefined(result.favorites)) {
upsertFavoritesInCache(result.favorites as Favorite[]);
}
-
- if (isDefined(result.favoriteFolders)) {
- upsertFavoritesFoldersInCache(result.favoriteFolders as FavoriteFolder[]);
- }
}, [
result,
upsertViewsInCache,
upsertFavoritesInCache,
- upsertFavoritesFoldersInCache,
+ isFavoriteFolderEnabled,
]);
return <>>;
diff --git a/packages/twenty-front/src/modules/prefetch/hooks/useIsFavoriteFoldersPrefetchLoading.ts b/packages/twenty-front/src/modules/prefetch/hooks/useIsFavoriteFoldersPrefetchLoading.ts
new file mode 100644
index 000000000000..a3ffaa608e09
--- /dev/null
+++ b/packages/twenty-front/src/modules/prefetch/hooks/useIsFavoriteFoldersPrefetchLoading.ts
@@ -0,0 +1,11 @@
+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;
+};
diff --git a/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts b/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
index ea3d4b31d7cb..ca3e275b5bf8 100644
--- a/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
+++ b/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
@@ -1,22 +1,25 @@
-import { useRecoilValue } from 'recoil';
-
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
+import { useRecoilValue } from 'recoil';
+import { useIsFavoriteFoldersPrefetchLoading } from './useIsFavoriteFoldersPrefetchLoading';
export const useIsPrefetchLoading = () => {
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
+ const isFavoriteFoldersLoading = useIsFavoriteFoldersPrefetchLoading();
+
const areViewsPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllViews),
);
const areFavoritesPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllFavorites),
);
- const areFavoritesFolderPrefetched = useRecoilValue(
- prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
- );
return (
!areViewsPrefetched ||
!areFavoritesPrefetched ||
- !areFavoritesFolderPrefetched
+ (isFavoriteFolderEnabled && isFavoriteFoldersLoading)
);
};
diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/PageFavoriteButton.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/PageFavoriteButton.tsx
index 21e5cfe874b2..2891c48f46f4 100644
--- a/packages/twenty-front/src/modules/ui/layout/page/components/PageFavoriteButton.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/page/components/PageFavoriteButton.tsx
@@ -2,14 +2,19 @@ import { IconButton, IconHeart } from 'twenty-ui';
type PageFavoriteButtonProps = {
isFavorite: boolean;
+ onClick?: () => void;
};
-export const PageFavoriteButton = ({ isFavorite }: PageFavoriteButtonProps) => (
+export const PageFavoriteButton = ({
+ isFavorite,
+ onClick,
+}: PageFavoriteButtonProps) => (
);
diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx
index 456ea5d35913..37bf95a6defe 100644
--- a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx
+++ b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx
@@ -32,6 +32,7 @@ export const RecordShowPage = () => {
isFavorite,
record,
objectMetadataItem,
+ handleFavoriteButtonClick,
} = useRecordShowPage(
parameters.objectNameSingular ?? '',
parameters.objectRecordId ?? '',
@@ -69,6 +70,7 @@ export const RecordShowPage = () => {
{...{
isFavorite,
record,
+ handleFavoriteButtonClick,
objectMetadataItem,
objectNameSingular,
}}
diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPageBaseHeader.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPageBaseHeader.tsx
index acdd4b271bf1..fdcb4909d01d 100644
--- a/packages/twenty-front/src/pages/object-record/RecordShowPageBaseHeader.tsx
+++ b/packages/twenty-front/src/pages/object-record/RecordShowPageBaseHeader.tsx
@@ -1,15 +1,18 @@
import { FAVORITE_FOLDERS_DROPDOWN_ID } from '@/favorites/constants/FavoriteFoldersDropdownId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
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 { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMoreButton';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
type RecordShowPageBaseHeaderProps = {
isFavorite: boolean;
record: ObjectRecord | undefined;
objectMetadataItem: ObjectMetadataItem;
objectNameSingular: string;
+ handleFavoriteButtonClick: () => void;
};
export const RecordShowPageBaseHeader = ({
@@ -17,16 +20,28 @@ export const RecordShowPageBaseHeader = ({
record,
objectMetadataItem,
objectNameSingular,
+ handleFavoriteButtonClick,
}: RecordShowPageBaseHeaderProps) => {
+ const isFavoriteFolderEnabled = useIsFeatureEnabled(
+ 'IS_FAVORITE_FOLDER_ENABLED',
+ );
+
return (
<>
-
+ {isFavoriteFolderEnabled ? (
+
+ ) : (
+
+ )}