diff --git a/apps/web/modules/more/more-page-view.tsx b/apps/web/modules/more/more-page-view.tsx index ae5ff2c01d0104..185ec080f9d45e 100644 --- a/apps/web/modules/more/more-page-view.tsx +++ b/apps/web/modules/more/more-page-view.tsx @@ -1,6 +1,7 @@ "use client"; -import Shell, { MobileNavigationMoreItems } from "@calcom/features/shell/Shell"; +import Shell from "@calcom/features/shell/Shell"; +import { MobileNavigationMoreItems } from "@calcom/features/shell/navigation/Navigation"; import { useLocale } from "@calcom/lib/hooks/useLocale"; export default function MorePage() { diff --git a/apps/web/playwright/signup.e2e.ts b/apps/web/playwright/signup.e2e.ts index 8684453d591aed..0475b06b376172 100644 --- a/apps/web/playwright/signup.e2e.ts +++ b/apps/web/playwright/signup.e2e.ts @@ -1,4 +1,5 @@ -import { expect, Page } from "@playwright/test"; +import type { Page } from "@playwright/test"; +import { expect } from "@playwright/test"; import { randomBytes } from "crypto"; import { APP_NAME, IS_PREMIUM_USERNAME_ENABLED, IS_MAILHOG_ENABLED } from "@calcom/lib/constants"; diff --git a/packages/features/auth/lib/hooks/useRedirectToLoginIfUnauthenticated.tsx b/packages/features/auth/lib/hooks/useRedirectToLoginIfUnauthenticated.tsx new file mode 100644 index 00000000000000..84cb2afdd9b68c --- /dev/null +++ b/packages/features/auth/lib/hooks/useRedirectToLoginIfUnauthenticated.tsx @@ -0,0 +1,28 @@ +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; + +import { WEBAPP_URL } from "@calcom/lib/constants"; + +export function useRedirectToLoginIfUnauthenticated(isPublic = false) { + const { data: session, status } = useSession(); + const loading = status === "loading"; + const router = useRouter(); + useEffect(() => { + if (isPublic) { + return; + } + + if (!loading && !session) { + const urlSearchParams = new URLSearchParams(); + urlSearchParams.set("callbackUrl", `${WEBAPP_URL}${location.pathname}${location.search}`); + router.replace(`/auth/login?${urlSearchParams.toString()}`); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loading, session, isPublic]); + + return { + loading: loading && !session, + session, + }; +} diff --git a/packages/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded.tsx b/packages/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded.tsx new file mode 100644 index 00000000000000..51355e5be9781e --- /dev/null +++ b/packages/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded.tsx @@ -0,0 +1,53 @@ +import type { User } from "@prisma/client"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; + +import dayjs from "@calcom/dayjs"; +import { useFlagMap } from "@calcom/features/flags/context/provider"; +import { useEmailVerifyCheck } from "@calcom/trpc/react/hooks/useEmailVerifyCheck"; +import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; + +const shouldShowOnboarding = ( + user: Pick & { + organizationId: number | null; + } +) => { + return ( + !user.completedOnboarding && + !user.organizationId && + dayjs(user.createdDate).isAfter(ONBOARDING_INTRODUCED_AT) + ); +}; + +export const ONBOARDING_INTRODUCED_AT = dayjs("September 1 2021").toISOString(); + +export const ONBOARDING_NEXT_REDIRECT = { + redirect: { + permanent: false, + destination: "/getting-started", + }, +} as const; + +export function useRedirectToOnboardingIfNeeded() { + const router = useRouter(); + const query = useMeQuery(); + const user = query.data; + const flags = useFlagMap(); + + const { data: email } = useEmailVerifyCheck(); + + const needsEmailVerification = !email?.isVerified && flags["email-verification"]; + + const isRedirectingToOnboarding = user && shouldShowOnboarding(user); + + useEffect(() => { + if (isRedirectingToOnboarding && !needsEmailVerification) { + router.replace("/getting-started"); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isRedirectingToOnboarding, needsEmailVerification]); + + return { + isRedirectingToOnboarding, + }; +} diff --git a/packages/features/ee/support/lib/intercom/useIntercom.ts b/packages/features/ee/support/lib/intercom/useIntercom.ts index b87367836f7c1b..d639137feb644a 100644 --- a/packages/features/ee/support/lib/intercom/useIntercom.ts +++ b/packages/features/ee/support/lib/intercom/useIntercom.ts @@ -1,11 +1,13 @@ // eslint-disable-next-line no-restricted-imports import { noop } from "lodash"; +import { useEffect } from "react"; import { useIntercom as useIntercomLib } from "react-use-intercom"; import { z } from "zod"; import dayjs from "@calcom/dayjs"; import { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants"; import { useHasTeamPlan, useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan"; +import { localStorage } from "@calcom/lib/webstorage"; import { trpc } from "@calcom/trpc/react"; // eslint-disable-next-line turbo/no-undeclared-env-vars @@ -117,4 +119,16 @@ export const useIntercom = () => { return { ...hookData, open, boot }; }; +export const useBootIntercom = () => { + const { boot } = useIntercom(); + const { data: user } = trpc.viewer.me.useQuery(); + useEffect(() => { + // not using useMediaQuery as it toggles between true and false + const showIntercom = localStorage.getItem("showIntercom"); + if (!isInterComEnabled || showIntercom === "false" || window.innerWidth <= 768 || !user) return; + boot(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [user]); +}; + export default useIntercom; diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index 60f4dc5c77c6f0..932c7860c9586f 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -1,246 +1,36 @@ -import type { User as UserAuth } from "next-auth"; -import { signOut, useSession } from "next-auth/react"; -import dynamic from "next/dynamic"; -import Link from "next/link"; +import { useSession } from "next-auth/react"; import { usePathname, useRouter } from "next/navigation"; import type { Dispatch, ReactElement, ReactNode, SetStateAction } from "react"; -import React, { cloneElement, Fragment, useEffect, useMemo, useState } from "react"; +import React, { cloneElement, Fragment } from "react"; import { Toaster } from "react-hot-toast"; -import dayjs from "@calcom/dayjs"; -import { useIsEmbed } from "@calcom/embed-core/embed-iframe"; -import UnconfirmedBookingBadge from "@calcom/features/bookings/UnconfirmedBookingBadge"; -import ImpersonatingBanner, { - type ImpersonatingBannerProps, -} from "@calcom/features/ee/impersonation/components/ImpersonatingBanner"; -import { - OrgUpgradeBanner, - type OrgUpgradeBannerProps, -} from "@calcom/features/ee/organizations/components/OrgUpgradeBanner"; -import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains"; -import HelpMenuItem from "@calcom/features/ee/support/components/HelpMenuItem"; -import useIntercom, { isInterComEnabled } from "@calcom/features/ee/support/lib/intercom/useIntercom"; -import { TeamsUpgradeBanner, type TeamsUpgradeBannerProps } from "@calcom/features/ee/teams/components"; -import { useFlagMap } from "@calcom/features/flags/context/provider"; -import { KBarContent, KBarRoot, KBarTrigger } from "@calcom/features/kbar/Kbar"; +import { useRedirectToLoginIfUnauthenticated } from "@calcom/features/auth/lib/hooks/useRedirectToLoginIfUnauthenticated"; +import { useRedirectToOnboardingIfNeeded } from "@calcom/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded"; +import { useBootIntercom } from "@calcom/features/ee/support/lib/intercom/useIntercom"; +import { KBarContent, KBarRoot } from "@calcom/features/kbar/Kbar"; import TimezoneChangeDialog from "@calcom/features/settings/TimezoneChangeDialog"; -import AdminPasswordBanner, { - type AdminPasswordBannerProps, -} from "@calcom/features/users/components/AdminPasswordBanner"; -import CalendarCredentialBanner, { - type CalendarCredentialBannerProps, -} from "@calcom/features/users/components/CalendarCredentialBanner"; -import { - InvalidAppCredentialBanners, - type InvalidAppCredentialBannersProps, -} from "@calcom/features/users/components/InvalidAppCredentialsBanner"; -import VerifyEmailBanner, { - type VerifyEmailBannerProps, -} from "@calcom/features/users/components/VerifyEmailBanner"; import classNames from "@calcom/lib/classNames"; -import { - APP_NAME, - DESKTOP_APP_LINK, - ENABLE_PROFILE_SWITCHER, - IS_CALCOM, - IS_VISUAL_REGRESSION_TESTING, - JOIN_COMMUNITY, - ROADMAP, - TOP_BANNER_HEIGHT, - WEBAPP_URL, -} from "@calcom/lib/constants"; -import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; +import { APP_NAME } from "@calcom/lib/constants"; import { useFormbricks } from "@calcom/lib/formbricks-client"; -import getBrandColours from "@calcom/lib/getBrandColours"; -import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl"; -import { useCopy } from "@calcom/lib/hooks/useCopy"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { ButtonState, useNotifications } from "@calcom/lib/hooks/useNotifications"; -import { useRefreshData } from "@calcom/lib/hooks/useRefreshData"; -import useTheme from "@calcom/lib/hooks/useTheme"; -import { isKeyInObject } from "@calcom/lib/isKeyInObject"; -import { localStorage } from "@calcom/lib/webstorage"; -import type { User } from "@calcom/prisma/client"; -import { trpc } from "@calcom/trpc/react"; -import useEmailVerifyCheck from "@calcom/trpc/react/hooks/useEmailVerifyCheck"; -import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; -import { - Avatar, - Button, - ButtonOrLink, - Credits, - Dropdown, - DropdownItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuPortal, - DropdownMenuSeparator, - DropdownMenuTrigger, - ErrorBoundary, - HeadSeo, - Icon, - Logo, - showToast, - SkeletonText, - Tooltip, - useCalcomTheme, - type IconName, -} from "@calcom/ui"; -import { useGetUserAttributes } from "@calcom/web/components/settings/platform/hooks/useGetUserAttributes"; +import { Button, ErrorBoundary, HeadSeo, SkeletonText } from "@calcom/ui"; -import { useOrgBranding } from "../ee/organizations/context/provider"; -import FreshChatProvider from "../ee/support/lib/freshchat/FreshChatProvider"; -import { TeamInviteBadge } from "./TeamInviteBadge"; - -// need to import without ssr to prevent hydration errors -const Tips = dynamic(() => import("@calcom/features/tips").then((mod) => mod.Tips), { - ssr: false, -}); - -/* TODO: Migate this */ - -export const ONBOARDING_INTRODUCED_AT = dayjs("September 1 2021").toISOString(); - -export const ONBOARDING_NEXT_REDIRECT = { - redirect: { - permanent: false, - destination: "/getting-started", - }, -} as const; - -export const shouldShowOnboarding = ( - user: Pick & { - organizationId: number | null; - } -) => { - return ( - !user.completedOnboarding && - !user.organizationId && - dayjs(user.createdDate).isAfter(ONBOARDING_INTRODUCED_AT) - ); -}; - -function useRedirectToLoginIfUnauthenticated(isPublic = false) { - const { data: session, status } = useSession(); - const loading = status === "loading"; - const router = useRouter(); - useEffect(() => { - if (isPublic) { - return; - } - - if (!loading && !session) { - const urlSearchParams = new URLSearchParams(); - urlSearchParams.set("callbackUrl", `${WEBAPP_URL}${location.pathname}${location.search}`); - router.replace(`/auth/login?${urlSearchParams.toString()}`); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, session, isPublic]); - - return { - loading: loading && !session, - session, - }; -} - -type BannerTypeProps = { - teamUpgradeBanner: TeamsUpgradeBannerProps; - orgUpgradeBanner: OrgUpgradeBannerProps; - verifyEmailBanner: VerifyEmailBannerProps; - adminPasswordBanner: AdminPasswordBannerProps; - impersonationBanner: ImpersonatingBannerProps; - calendarCredentialBanner: CalendarCredentialBannerProps; - invalidAppCredentialBanners: InvalidAppCredentialBannersProps; -}; - -type BannerType = keyof BannerTypeProps; - -type BannerComponent = { - [Key in BannerType]: (props: BannerTypeProps[Key]) => JSX.Element; -}; - -const BannerComponent: BannerComponent = { - teamUpgradeBanner: (props: TeamsUpgradeBannerProps) => , - orgUpgradeBanner: (props: OrgUpgradeBannerProps) => , - verifyEmailBanner: (props: VerifyEmailBannerProps) => , - adminPasswordBanner: (props: AdminPasswordBannerProps) => , - impersonationBanner: (props: ImpersonatingBannerProps) => , - calendarCredentialBanner: (props: CalendarCredentialBannerProps) => , - invalidAppCredentialBanners: (props: InvalidAppCredentialBannersProps) => ( - - ), -}; - -function useRedirectToOnboardingIfNeeded() { - const router = useRouter(); - const query = useMeQuery(); - const user = query.data; - const flags = useFlagMap(); - - const { data: email } = useEmailVerifyCheck(); - - const needsEmailVerification = !email?.isVerified && flags["email-verification"]; - - const isRedirectingToOnboarding = user && shouldShowOnboarding(user); - - useEffect(() => { - if (isRedirectingToOnboarding && !needsEmailVerification) { - router.replace("/getting-started"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isRedirectingToOnboarding, needsEmailVerification]); - - return { - isRedirectingToOnboarding, - }; -} - -type allBannerProps = { [Key in BannerType]: BannerTypeProps[Key]["data"] }; - -const useBanners = () => { - const { data: getUserTopBanners, isPending } = trpc.viewer.getUserTopBanners.useQuery(); - const { data: userSession } = useSession(); - - if (isPending || !userSession) return null; - - const isUserInactiveAdmin = userSession?.user.role === "INACTIVE_ADMIN"; - const userImpersonatedByUID = userSession?.user.impersonatedBy?.id; - - const userSessionBanners = { - adminPasswordBanner: isUserInactiveAdmin ? userSession : null, - impersonationBanner: userImpersonatedByUID ? userSession : null, - }; - - const allBanners: allBannerProps = Object.assign({}, getUserTopBanners, userSessionBanners); - - return allBanners; -}; +import { SideBarContainer } from "./SideBar"; +import { TopNavContainer } from "./TopNav"; +import { BannerContainer } from "./banners/LayoutBanner"; +import { useBanners } from "./banners/useBanners"; +import { MobileNavigationContainer } from "./navigation/Navigation"; +import { useAppTheme } from "./useAppTheme"; const Layout = (props: LayoutProps) => { - const banners = useBanners(); + const { banners, bannersHeight } = useBanners(); const pathname = usePathname(); + const isFullPageWithoutSidebar = pathname?.startsWith("/apps/routing-forms/reporting/"); - const { data: user } = trpc.viewer.me.useQuery(); - const { boot } = useIntercom(); const pageTitle = typeof props.heading === "string" && !props.title ? props.heading : props.title; - useEffect(() => { - // not using useMediaQuery as it toggles between true and false - const showIntercom = localStorage.getItem("showIntercom"); - if (!isInterComEnabled || showIntercom === "false" || window.innerWidth <= 768 || !user) return; - boot(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [user]); - - const bannersHeight = useMemo(() => { - const activeBanners = - banners && - Object.entries(banners).filter(([_, value]) => { - return value && (!Array.isArray(value) || value.length > 0); - }); - return (activeBanners?.length ?? 0) * TOP_BANNER_HEIGHT; - }, [banners]); - + useBootIntercom(); useFormbricks(); return ( @@ -259,32 +49,7 @@ const Layout = (props: LayoutProps) => {
{banners && !props.isPlatformUser && !isFullPageWithoutSidebar && ( -
- {Object.keys(banners).map((key) => { - if (key === "teamUpgradeBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "orgUpgradeBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "verifyEmailBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "adminPasswordBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "impersonationBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "calendarCredentialBanner") { - const Banner = BannerComponent[key]; - return ; - } else if (key === "invalidAppCredentialBanners") { - const Banner = BannerComponent[key]; - return ; - } - })} -
+ )}
@@ -335,16 +100,6 @@ export type LayoutProps = { isPlatformUser?: boolean; }; -const useAppTheme = () => { - const { data: user } = useMeQuery(); - const brandTheme = getBrandColours({ - lightVal: user?.brandColor, - darkVal: user?.darkBrandColor, - }); - useCalcomTheme(brandTheme); - useTheme(user?.appTheme); -}; - const KBarWrapper = ({ children, withKBar = false }: { withKBar: boolean; children: React.ReactNode }) => withKBar ? ( @@ -379,753 +134,6 @@ export default function Shell(props: LayoutProps) { ); } -interface UserDropdownProps { - small?: boolean; -} - -function UserDropdown({ small }: UserDropdownProps) { - const { isPlatformUser } = useGetUserAttributes(); - const { t } = useLocale(); - const { data: user } = useMeQuery(); - const utils = trpc.useUtils(); - const bookerUrl = useBookerUrl(); - const pathname = usePathname(); - const isPlatformPages = pathname?.startsWith("/settings/platform"); - useEffect(() => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const Beacon = window.Beacon; - // window.Beacon is defined when user actually opens up HelpScout and username is available here. On every re-render update session info, so that it is always latest. - Beacon && - Beacon("session-data", { - username: user?.username || "Unknown", - screenResolution: `${screen.width}x${screen.height}`, - }); - }); - - const [helpOpen, setHelpOpen] = useState(false); - const [menuOpen, setMenuOpen] = useState(false); - - const onHelpItemSelect = () => { - setHelpOpen(false); - setMenuOpen(false); - }; - - // Prevent rendering dropdown if user isn't available. - // We don't want to show nameless user. - if (!user) { - return null; - } - - return ( - - setMenuOpen((menuOpen) => !menuOpen)}> - - - - - - { - setMenuOpen(false); - setHelpOpen(false); - }} - className="group overflow-hidden rounded-md"> - {helpOpen ? ( - onHelpItemSelect()} /> - ) : ( - <> - {!isPlatformPages && ( - <> - - - - - - - - - - - - )} - - - - {t("join_our_community")} - - - - - {t("visit_roadmap")} - - - - - - {!isPlatformPages && ( - - - {t("download_desktop_app")} - - - )} - - {!isPlatformPages && isPlatformUser && ( - - - Platform - - - )} - - - - - - - )} - - - - - ); -} - -export type NavigationItemType = { - name: string; - href: string; - isLoading?: boolean; - onClick?: React.MouseEventHandler; - target?: HTMLAnchorElement["target"]; - badge?: React.ReactNode; - icon?: IconName; - child?: NavigationItemType[]; - pro?: true; - onlyMobile?: boolean; - onlyDesktop?: boolean; - isCurrent?: ({ - item, - isChild, - pathname, - }: { - item: Pick; - isChild?: boolean; - pathname: string | null; - }) => boolean; -}; - -const requiredCredentialNavigationItems = ["Routing Forms"]; -const MORE_SEPARATOR_NAME = "more"; - -const navigation: NavigationItemType[] = [ - { - name: "event_types_page_title", - href: "/event-types", - icon: "link", - }, - { - name: "bookings", - href: "/bookings/upcoming", - icon: "calendar", - badge: , - isCurrent: ({ pathname }) => pathname?.startsWith("/bookings") ?? false, - }, - { - name: "availability", - href: "/availability", - icon: "clock", - }, - { - name: "teams", - href: "/teams", - icon: "users", - onlyDesktop: true, - badge: , - }, - { - name: "apps", - href: "/apps", - icon: "grid-3x3", - isCurrent: ({ pathname: path, item }) => { - // During Server rendering path is /v2/apps but on client it becomes /apps(weird..) - return (path?.startsWith(item.href) ?? false) && !(path?.includes("routing-forms/") ?? false); - }, - child: [ - { - name: "app_store", - href: "/apps", - isCurrent: ({ pathname: path, item }) => { - // During Server rendering path is /v2/apps but on client it becomes /apps(weird..) - return ( - (path?.startsWith(item.href) ?? false) && - !(path?.includes("routing-forms/") ?? false) && - !(path?.includes("/installed") ?? false) - ); - }, - }, - { - name: "installed_apps", - href: "/apps/installed/calendar", - isCurrent: ({ pathname: path }) => - (path?.startsWith("/apps/installed/") ?? false) || - (path?.startsWith("/v2/apps/installed/") ?? false), - }, - ], - }, - { - name: MORE_SEPARATOR_NAME, - href: "/more", - icon: "ellipsis", - }, - { - name: "Routing Forms", - href: "/apps/routing-forms/forms", - icon: "file-text", - isCurrent: ({ pathname }) => pathname?.startsWith("/apps/routing-forms/") ?? false, - }, - { - name: "workflows", - href: "/workflows", - icon: "zap", - }, - { - name: "insights", - href: "/insights", - icon: "chart-bar", - }, -]; - -const platformNavigation: NavigationItemType[] = [ - { - name: "Dashboard", - href: "/settings/platform/", - icon: "layout-dashboard", - }, - { - name: "Documentation", - href: "https://docs.cal.com/docs/platform", - icon: "chart-bar", - target: "_blank", - }, - { - name: "API reference", - href: "https://api.cal.com/v2/docs#/", - icon: "terminal", - target: "_blank", - }, - { - name: "Atoms", - href: "https://docs.cal.com/docs/platform#atoms", - icon: "atom", - target: "_blank", - }, - { - name: MORE_SEPARATOR_NAME, - href: "https://docs.cal.com/docs/platform/faq", - icon: "ellipsis", - target: "_blank", - }, - { - name: "Billing", - href: "/settings/platform/billing", - icon: "chart-bar", - }, -]; - -const getDesktopNavigationItems = (isPlatformNavigation = false) => { - const navigationType = !isPlatformNavigation ? navigation : platformNavigation; - const moreSeparatorIndex = navigationType.findIndex((item) => item.name === MORE_SEPARATOR_NAME); - - const { desktopNavigationItems, mobileNavigationBottomItems, mobileNavigationMoreItems } = ( - !isPlatformNavigation ? navigation : platformNavigation - ).reduce>( - (items, item, index) => { - // We filter out the "more" separator in` desktop navigation - if (item.name !== MORE_SEPARATOR_NAME) items.desktopNavigationItems.push(item); - // Items for mobile bottom navigation - if (index < moreSeparatorIndex + 1 && !item.onlyDesktop) { - items.mobileNavigationBottomItems.push(item); - } // Items for the "more" menu in mobile navigation - else { - items.mobileNavigationMoreItems.push(item); - } - return items; - }, - { desktopNavigationItems: [], mobileNavigationBottomItems: [], mobileNavigationMoreItems: [] } - ); - - return { desktopNavigationItems, mobileNavigationBottomItems, mobileNavigationMoreItems }; -}; - -const Navigation = ({ isPlatformNavigation = false }: { isPlatformNavigation?: boolean }) => { - const { desktopNavigationItems } = getDesktopNavigationItems(isPlatformNavigation); - - return ( - - ); -}; - -function useShouldDisplayNavigationItem(item: NavigationItemType) { - const flags = useFlagMap(); - if (isKeyInObject(item.name, flags)) return flags[item.name]; - return true; -} - -const defaultIsCurrent: NavigationItemType["isCurrent"] = ({ isChild, item, pathname }) => { - return isChild ? item.href === pathname : item.href ? pathname?.startsWith(item.href) ?? false : false; -}; - -const NavigationItem: React.FC<{ - index?: number; - item: NavigationItemType; - isChild?: boolean; -}> = (props) => { - const { item, isChild } = props; - const { t, isLocaleReady } = useLocale(); - const pathname = usePathname(); - const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; - const current = isCurrent({ isChild: !!isChild, item, pathname }); - const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); - - if (!shouldDisplayNavigationItem) return null; - - return ( - - - - {item.icon && ( - - {item.child && - isCurrent({ pathname, isChild, item }) && - item.child.map((item, index) => )} - - ); -}; - -function MobileNavigationContainer({ isPlatformNavigation = false }: { isPlatformNavigation?: boolean }) { - const { status } = useSession(); - if (status !== "authenticated") return null; - return ; -} - -const MobileNavigation = ({ isPlatformNavigation = false }: { isPlatformNavigation?: boolean }) => { - const isEmbed = useIsEmbed(); - const { mobileNavigationBottomItems } = getDesktopNavigationItems(isPlatformNavigation); - - return ( - <> - - {/* add padding to content for mobile navigation*/} -
- - ); -}; - -const MobileNavigationItem: React.FC<{ - item: NavigationItemType; - isChild?: boolean; -}> = (props) => { - const { item, isChild } = props; - const pathname = usePathname(); - const { t, isLocaleReady } = useLocale(); - const isCurrent: NavigationItemType["isCurrent"] = item.isCurrent || defaultIsCurrent; - const current = isCurrent({ isChild: !!isChild, item, pathname }); - const shouldDisplayNavigationItem = useShouldDisplayNavigationItem(props.item); - - if (!shouldDisplayNavigationItem) return null; - return ( - - {item.badge &&
{item.badge}
} - {item.icon && ( -