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;