import { TooltipProvider } from "@radix-ui/react-tooltip"; import type { Session } from "next-auth"; import { useSession } from "next-auth/react"; import { ThemeProvider } from "next-themes"; import type { AppProps as NextAppProps } from "next/app"; import type { ReadonlyURLSearchParams } from "next/navigation"; import { usePathname, useSearchParams } from "next/navigation"; import { NuqsAdapter } from "nuqs/adapters/next/app"; import DynamicPostHogProvider from "~/ee/posthog/providerDynamic"; import DynamicPostHogPageView from "~/ee/posthog/pageViewDynamic"; import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider"; import { FeatureProvider } from "@calcom/features/flags/context/provider"; import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags"; import DynamicHelpscoutProvider from "@calcom/web/modules/ee/support/lib/helpscout/providerDynamic"; import DynamicIntercomProvider from "@calcom/web/modules/ee/support/lib/intercom/providerDynamic"; import useIsBookingPage from "@lib/hooks/useIsBookingPage"; import useIsThemeSupported from "@lib/hooks/useIsThemeSupported"; import { useNuqsParams } from "@lib/hooks/useNuqsParams"; import type { WithLocaleProps } from "@lib/withLocale"; import type { PageWrapperProps } from "@components/PageWrapperAppDir"; import { getThemeProviderProps } from "./getThemeProviderProps"; // Workaround for https://github.com/vercel/next.js/issues/8592 export type AppProps = Omit< NextAppProps< WithLocaleProps<{ nonce: string | undefined; themeBasis?: string; session: Session; }> >, "Component" > & { Component: NextAppProps["Component"] & { requiresLicense?: boolean; isBookingPage?: boolean | ((arg: { router: NextAppProps["router"] }) => boolean); PageWrapper?: (props: AppProps) => JSX.Element; }; /** Will be defined only is there was an error */ err?: Error; }; const getEmbedNamespace = (searchParams: ReadonlyURLSearchParams) => { // Mostly embed query param should be available on server. Use that there. // Use the most reliable detection on client return typeof window !== "undefined" ? window.getEmbedNamespace() : (searchParams.get("embed") ?? null); }; type CalcomThemeProps = Readonly<{ isBookingPage: boolean; nonce: string | undefined; children: React.ReactNode; isThemeSupported: boolean; }>; const CalcomThemeProvider = (props: CalcomThemeProps) => { // Use namespace of embed to ensure same namespaced embed are displayed with same theme. This allows different embeds on the same website to be themed differently // One such example is our Embeds Demo and Testing page at http://localhost:3100 // Having `getEmbedNamespace` defined on window before react initializes the app, ensures that embedNamespace is available on the first mount and can be used as part of storageKey const searchParams = useSearchParams(); const embedNamespace = searchParams ? getEmbedNamespace(searchParams) : null; const isEmbedMode = typeof embedNamespace === "string"; const pathname = usePathname(); const { key, ...themeProviderProps } = getThemeProviderProps({ props, isEmbedMode, embedNamespace, pathname, searchParams, }); return ( {/* Embed Mode can be detected reliably only on client side here as there can be static generated pages as well which can't determine if it's embed mode at backend */} {/* color-scheme makes background:transparent not work in iframe which is required by embed. */} {typeof window !== "undefined" && !isEmbedMode && ( //eslint-disable-next-line react/no-unknown-property )} {props.children} ); }; function FeatureFlagsProvider({ children }: { children: React.ReactNode }) { const flags = useFlags(); return {children}; } function useOrgBrandingValues() { const session = useSession(); return session?.data?.user.org; } function OrgBrandProvider({ children }: { children: React.ReactNode }) { const orgBrand = useOrgBrandingValues(); return {children}; } const AppProviders = (props: PageWrapperProps) => { // No need to have intercom on public pages - Good for Page Performance const isBookingPage = useIsBookingPage(); const isThemeSupported = useIsThemeSupported(); const nuqsParams = useNuqsParams(); const RemainingProviders = ( <> {/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */} {props.isBookingPage || isBookingPage ? ( {props.children} ) : ( {props.children} )} ); if (props.isBookingPage || isBookingPage) { return RemainingProviders; } return ( <> {RemainingProviders} ); }; export default AppProviders;