diff --git a/packages/constants/src/workspace.ts b/packages/constants/src/workspace.ts index cddc1a56ce7..06c9fb659bb 100644 --- a/packages/constants/src/workspace.ts +++ b/packages/constants/src/workspace.ts @@ -325,6 +325,7 @@ export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarN WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["projects"], ]; +export const IS_FAVORITE_MENU_OPEN = "is_favorite_menu_open"; export const WORKSPACE_DEFAULT_SEARCH_RESULT: IWorkspaceSearchResults = { results: { workspace: [], diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index b7fc40666f4..bc627fdf28c 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -6,7 +6,14 @@ import { useParams, usePathname, useSearchParams } from "next/navigation"; import { useForm } from "react-hook-form"; import { Eye, Users } from "lucide-react"; // types -import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { + CYCLE_FAVORITED, + CYCLE_UNFAVORITED, + EUserPermissions, + EUserPermissionsLevel, + IS_FAVORITE_MENU_OPEN, +} from "@plane/constants"; +import { useLocalStorage } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; import { ICycle, TCycleGroups } from "@plane/types"; // ui @@ -15,8 +22,6 @@ import { Avatar, AvatarGroup, FavoriteStar, LayersIcon, Tooltip, TransferIcon, s import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles"; import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; -// constants -// helpers import { getDate } from "@/helpers/date-time.helper"; import { getFileURL } from "@/helpers/file.helper"; // hooks @@ -59,6 +64,12 @@ export const CycleListItemAction: FC = observer((props) => { const { captureEvent } = useEventTracker(); const { allowPermissions } = useUserPermissions(); + // local storage + const { setValue: toggleFavoriteMenu, storedValue: isFavoriteMenuOpen } = useLocalStorage( + IS_FAVORITE_MENU_OPEN, + false + ); + const { getUserDetails } = useMember(); // form @@ -91,6 +102,7 @@ export const CycleListItemAction: FC = observer((props) => { const addToFavoritePromise = addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycleId).then( () => { + if (!isFavoriteMenuOpen) toggleFavoriteMenu(true); captureEvent(CYCLE_FAVORITED, { cycle_id: cycleId, element: "List layout", diff --git a/web/core/components/modules/module-card-item.tsx b/web/core/components/modules/module-card-item.tsx index 9585755eef3..2204738cf3b 100644 --- a/web/core/components/modules/module-card-item.tsx +++ b/web/core/components/modules/module-card-item.tsx @@ -13,7 +13,9 @@ import { MODULE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel, + IS_FAVORITE_MENU_OPEN, } from "@plane/constants"; +import { useLocalStorage } from "@plane/hooks"; import { IModule } from "@plane/types"; import { Card, @@ -30,7 +32,6 @@ import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ModuleQuickActions } from "@/components/modules"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; -// constants // helpers import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; import { generateQueryParams } from "@/helpers/router.helper"; @@ -59,6 +60,9 @@ export const ModuleCardItem: React.FC = observer((props) => { const { getUserDetails } = useMember(); const { captureEvent } = useEventTracker(); + // local storage + const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage(IS_FAVORITE_MENU_OPEN, false); + // derived values const moduleDetails = getModuleById(moduleId); const isEditingAllowed = allowPermissions( @@ -76,6 +80,7 @@ export const ModuleCardItem: React.FC = observer((props) => { const addToFavoritePromise = addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).then( () => { + if (!storedValue) toggleFavoriteMenu(true); captureEvent(MODULE_FAVORITED, { module_id: moduleId, element: "Grid layout", diff --git a/web/core/components/modules/module-list-item-action.tsx b/web/core/components/modules/module-list-item-action.tsx index 1f929751dfc..7a1b37a65ce 100644 --- a/web/core/components/modules/module-list-item-action.tsx +++ b/web/core/components/modules/module-list-item-action.tsx @@ -12,7 +12,9 @@ import { MODULE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel, + IS_FAVORITE_MENU_OPEN, } from "@plane/constants"; +import { useLocalStorage } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; import { IModule } from "@plane/types"; // ui @@ -45,6 +47,8 @@ export const ModuleListItemAction: FC = observer((props) => { const { t } = useTranslation(); + // local storage + const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage(IS_FAVORITE_MENU_OPEN, false); // derived values const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); @@ -63,6 +67,8 @@ export const ModuleListItemAction: FC = observer((props) => { const addToFavoritePromise = addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).then( () => { + // open favorites menu if closed + if (!storedValue) toggleFavoriteMenu(true); captureEvent(MODULE_FAVORITED, { module_id: moduleId, element: "Grid layout", diff --git a/web/core/components/pages/editor/header/extra-options.tsx b/web/core/components/pages/editor/header/extra-options.tsx index dfcfee53ca4..0b81d14522e 100644 --- a/web/core/components/pages/editor/header/extra-options.tsx +++ b/web/core/components/pages/editor/header/extra-options.tsx @@ -1,8 +1,12 @@ "use client"; import { observer } from "mobx-react"; +// constants +import { IS_FAVORITE_MENU_OPEN } from "@plane/constants"; // editor import { EditorRefApi } from "@plane/editor"; +// plane hooks +import { useLocalStorage } from "@plane/hooks"; // ui import { ArchiveIcon, FavoriteStar, setToast, TOAST_TYPE, Tooltip } from "@plane/ui"; // components @@ -37,6 +41,11 @@ export const PageExtraOptions: React.FC = observer((props) => { } = page; // use online status const { isOnline } = useOnlineStatus(); + // local storage + const { setValue: toggleFavoriteMenu, storedValue: isFavoriteMenuOpen } = useLocalStorage( + IS_FAVORITE_MENU_OPEN, + false + ); // favorite handler const handleFavorite = () => { if (is_favorite) { @@ -48,13 +57,14 @@ export const PageExtraOptions: React.FC = observer((props) => { }) ); } else { - addToFavorites().then(() => + addToFavorites().then(() => { + if (!isFavoriteMenuOpen) toggleFavoriteMenu(true); setToast({ type: TOAST_TYPE.SUCCESS, title: "Success!", message: "Page added to favorites.", - }) - ); + }); + }); } }; diff --git a/web/core/components/project/card.tsx b/web/core/components/project/card.tsx index 304ecab3351..73b7c641df7 100644 --- a/web/core/components/project/card.tsx +++ b/web/core/components/project/card.tsx @@ -6,7 +6,8 @@ import Link from "next/link"; import { useParams } from "next/navigation"; import { ArchiveRestoreIcon, Check, ExternalLink, LinkIcon, Lock, Settings, Trash2, UserPlus } from "lucide-react"; // types -import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants"; +import { useLocalStorage } from "@plane/hooks"; import type { IProject } from "@plane/types"; // ui import { @@ -68,6 +69,11 @@ export const ProjectCard: React.FC = observer((props) => { const hasMemberRole = project.member_role === EUserPermissions.MEMBER; // archive const isArchived = !!project.archived_at; + // local storage + const { setValue: toggleFavoriteMenu, storedValue: isFavoriteMenuOpen } = useLocalStorage( + IS_FAVORITE_MENU_OPEN, + false + ); const handleAddToFavorites = () => { if (!workspaceSlug) return; @@ -78,6 +84,10 @@ export const ProjectCard: React.FC = observer((props) => { success: { title: "Success!", message: () => "Project added to favorites.", + actionItems: () => { + if (!isFavoriteMenuOpen) toggleFavoriteMenu(true); + return <>; + }, }, error: { title: "Error!", diff --git a/web/core/components/views/view-list-item-action.tsx b/web/core/components/views/view-list-item-action.tsx index 9ec4a0288e7..eaeb412efc0 100644 --- a/web/core/components/views/view-list-item-action.tsx +++ b/web/core/components/views/view-list-item-action.tsx @@ -3,13 +3,13 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { Earth, Lock } from "lucide-react"; // types -import { EViewAccess, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { EViewAccess, EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants"; +import { useLocalStorage } from "@plane/hooks"; import { IProjectView } from "@plane/types"; // ui import { Tooltip, FavoriteStar } from "@plane/ui"; // components import { DeleteProjectViewModal, CreateUpdateProjectViewModal, ViewQuickActions } from "@/components/views"; -// constants // helpers import { calculateTotalFilters } from "@/helpers/filter.helper"; import { getPublishViewLink } from "@/helpers/project-views.helpers"; @@ -37,6 +37,12 @@ export const ViewListItemAction: FC = observer((props) => { const { addViewToFavorites, removeViewFromFavorites } = useProjectView(); const { getUserDetails } = useMember(); + // local storage + const { setValue: toggleFavoriteMenu, storedValue: isFavoriteOpen } = useLocalStorage( + IS_FAVORITE_MENU_OPEN, + false + ); + // derived values const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -50,10 +56,11 @@ export const ViewListItemAction: FC = observer((props) => { const publishLink = getPublishViewLink(view?.anchor); // handlers - const handleAddToFavorites = () => { + const handleAddToFavorites = async () => { if (!workspaceSlug || !projectId) return; - addViewToFavorites(workspaceSlug.toString(), projectId.toString(), view.id); + await addViewToFavorites(workspaceSlug.toString(), projectId.toString(), view.id); + if (!isFavoriteOpen) toggleFavoriteMenu(true); }; const handleRemoveFromFavorites = () => { diff --git a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx index bbb98aab48a..bfd08056da0 100644 --- a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx @@ -13,6 +13,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { ChevronRight, FolderPlus } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; +import { IS_FAVORITE_MENU_OPEN } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; // ui import { IFavorite } from "@plane/types"; @@ -54,7 +55,7 @@ export const SidebarFavoritesMenu = observer(() => { const { isMobile } = usePlatformOS(); // local storage - const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage("is_favorite_menu_open", false); + const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage(IS_FAVORITE_MENU_OPEN, false); // derived values const isFavoriteMenuOpen = !!storedValue; // refs diff --git a/web/core/hooks/use-page-operations.ts b/web/core/hooks/use-page-operations.ts index d16fd1c04f3..85862d828ba 100644 --- a/web/core/hooks/use-page-operations.ts +++ b/web/core/hooks/use-page-operations.ts @@ -1,4 +1,6 @@ import { useMemo } from "react"; +// plane constants +import { IS_FAVORITE_MENU_OPEN } from "@plane/constants"; // plane editor import { EditorRefApi } from "@plane/editor"; // plane types @@ -11,6 +13,8 @@ import { copyUrlToClipboard } from "@/helpers/string.helper"; import { useCollaborativePageActions } from "@/hooks/use-collaborative-page-actions"; // store types import { TPageInstance } from "@/store/pages/base-page"; +// local storage +import useLocalStorage from "./use-local-storage"; export type TPageOperations = { toggleLock: () => void; @@ -46,6 +50,11 @@ export const usePageOperations = ( } = page; // collaborative actions const { executeCollaborativeAction } = useCollaborativePageActions(props); + // local storage + const { setValue: toggleFavoriteMenu, storedValue: isfavoriteMenuOpen } = useLocalStorage( + IS_FAVORITE_MENU_OPEN, + false + ); // page operations const pageOperations: TPageOperations = useMemo(() => { const pageLink = getRedirectionLink(); @@ -141,13 +150,14 @@ export const usePageOperations = ( }) ); } else { - addToFavorites().then(() => + addToFavorites().then(() => { + if (!isfavoriteMenuOpen) toggleFavoriteMenu(true); setToast({ type: TOAST_TYPE.SUCCESS, title: "Success!", message: "Page added to favorites.", - }) - ); + }); + }); } }, toggleLock: async () => {