2022-08-17 17:38:21 +00:00
import { TooltipProvider } from "@radix-ui/react-tooltip" ;
2022-01-07 20:23:37 +00:00
import { SessionProvider } from "next-auth/react" ;
2022-07-28 19:58:26 +00:00
import { EventCollectionProvider } from "next-collect/client" ;
2023-02-16 22:39:57 +00:00
import type { SSRConfig } from "next-i18next" ;
import { appWithTranslation } from "next-i18next" ;
2022-07-28 19:58:26 +00:00
import { ThemeProvider } from "next-themes" ;
2022-05-17 19:31:49 +00:00
import type { AppProps as NextAppProps , AppProps as NextJsAppProps } from "next/app" ;
2023-02-16 22:39:57 +00:00
import type { NextRouter } from "next/router" ;
2023-04-05 18:14:46 +00:00
import { useRouter } from "next/router" ;
import type { ComponentProps , PropsWithChildren , ReactNode } from "react" ;
2021-08-27 12:11:24 +00:00
2022-07-28 19:58:26 +00:00
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic" ;
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic" ;
2023-03-25 00:59:04 +00:00
import { FeatureProvider } from "@calcom/features/flags/context/provider" ;
import { useFlags } from "@calcom/features/flags/hooks" ;
2022-11-10 23:40:01 +00:00
import { trpc } from "@calcom/trpc/react" ;
2022-11-23 02:55:25 +00:00
import { MetaProvider } from "@calcom/ui" ;
2021-09-24 20:02:03 +00:00
2022-02-16 15:26:48 +00:00
import usePublicPage from "@lib/hooks/usePublicPage" ;
2023-02-16 22:39:57 +00:00
import type { WithNonceProps } from "@lib/withNonce" ;
2021-09-22 19:52:38 +00:00
2022-11-05 18:00:10 +00:00
const I18nextAdapter = appWithTranslation < NextJsAppProps < SSRConfig > & { children : React.ReactNode } > (
( { children } ) = > < > { children } < / >
) ;
2021-10-14 10:57:49 +00:00
2021-10-20 16:00:11 +00:00
// Workaround for https://github.com/vercel/next.js/issues/8592
2023-02-06 22:50:08 +00:00
export type AppProps = Omit < NextAppProps < WithNonceProps & Record < string , unknown > > , "Component" > & {
2022-07-28 10:50:25 +00:00
Component : NextAppProps [ "Component" ] & {
requiresLicense? : boolean ;
2023-04-17 12:16:54 +00:00
isThemeSupported? : boolean ;
isBookingPage? : boolean | ( ( arg : { router : NextRouter } ) = > boolean ) ;
2022-09-02 19:00:41 +00:00
getLayout ? : ( page : React.ReactElement , router : NextRouter ) = > ReactNode ;
2023-04-18 18:45:32 +00:00
PageWrapper ? : ( props : AppProps ) = > JSX . Element ;
2022-07-28 10:50:25 +00:00
} ;
2023-02-06 22:50:08 +00:00
2021-10-20 16:00:11 +00:00
/** Will be defined only is there was an error */
err? : Error ;
} ;
type AppPropsWithChildren = AppProps & {
children : ReactNode ;
} ;
const CustomI18nextProvider = ( props : AppPropsWithChildren ) = > {
2022-07-18 18:59:51 +00:00
/ * *
* i18n should never be clubbed with other queries , so that it ' s caching can be managed independently .
* We intend to not cache i18n query
* * /
2022-11-10 23:40:01 +00:00
const { i18n , locale } = trpc . viewer . public . i18n . useQuery ( undefined , {
trpc : { context : { skipBatch : true } } ,
} ) . data ? ? {
2021-11-03 14:02:17 +00:00
locale : "en" ,
} ;
2021-10-20 16:00:11 +00:00
2021-10-14 10:57:49 +00:00
const passedProps = {
. . . props ,
2021-10-20 16:00:11 +00:00
pageProps : {
. . . props . pageProps ,
. . . i18n ,
} ,
router : locale ? { locale } : props . router ,
2021-10-14 10:57:49 +00:00
} as unknown as ComponentProps < typeof I18nextAdapter > ;
return < I18nextAdapter { ...passedProps } / > ;
} ;
2023-04-17 12:16:54 +00:00
const enum ThemeSupport {
// e.g. Login Page
None = "none" ,
// Entire App except Booking Pages
App = "systemOnly" ,
// Booking Pages(including Routing Forms)
Booking = "userConfigured" ,
}
2023-04-05 18:14:46 +00:00
const CalcomThemeProvider = (
props : PropsWithChildren <
2023-04-17 12:16:54 +00:00
WithNonceProps & {
isBookingPage? : boolean | ( ( arg : { router : NextRouter } ) = > boolean ) ;
isThemeSupported? : boolean ;
}
2023-04-05 18:14:46 +00:00
>
) = > {
// We now support the inverse of how we handled it in the past. Setting this to false will disable theme.
// undefined or true means we use system theme
const router = useRouter ( ) ;
2023-04-17 12:16:54 +00:00
const isBookingPage = ( ( ) = > {
if ( typeof props . isBookingPage === "function" ) {
return props . isBookingPage ( { router : router } ) ;
2023-04-05 18:14:46 +00:00
}
2023-04-17 12:16:54 +00:00
return props . isBookingPage ;
2023-04-05 18:14:46 +00:00
} ) ( ) ;
2023-04-17 12:16:54 +00:00
const themeSupport = isBookingPage
? ThemeSupport . Booking
: // if isThemeSupported is explicitly false, we don't use theme there
props . isThemeSupported === false
? ThemeSupport . None
: ThemeSupport . App ;
const forcedTheme = themeSupport === ThemeSupport . None ? "light" : undefined ;
2023-04-05 18:14:46 +00:00
// 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 embedNamespace = typeof window !== "undefined" ? window . getEmbedNamespace ( ) : null ;
2023-04-12 11:16:59 +00:00
const isEmbedMode = typeof embedNamespace === "string" ;
2023-04-17 12:16:54 +00:00
2023-04-12 11:16:59 +00:00
const storageKey = isEmbedMode
? ` embed-theme- ${ embedNamespace } `
2023-04-17 12:16:54 +00:00
: themeSupport === ThemeSupport . App
? "app-theme"
: themeSupport === ThemeSupport . Booking
? "booking-theme"
: undefined ;
2023-04-05 18:14:46 +00:00
return (
< ThemeProvider
nonce = { props . nonce }
enableColorScheme = { false }
2023-04-17 12:16:54 +00:00
enableSystem = { themeSupport !== ThemeSupport . None }
2023-04-05 18:14:46 +00:00
forcedTheme = { forcedTheme }
storageKey = { storageKey }
2023-04-17 12:16:54 +00:00
// next-themes doesn't listen to changes on storageKey. So we need to force a re-render when storageKey changes
// This is how login to dashboard soft navigation changes theme from light to dark
key = { storageKey }
2023-04-05 18:14:46 +00:00
attribute = "class" >
2023-04-12 11:16:59 +00:00
{ /* 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 && (
< style jsx global >
{ `
. dark {
color - scheme : dark ;
}
` }
< / style >
) }
2023-04-05 18:14:46 +00:00
{ props . children }
< / ThemeProvider >
) ;
} ;
2023-03-25 00:59:04 +00:00
function FeatureFlagsProvider ( { children } : { children : React.ReactNode } ) {
const flags = useFlags ( ) ;
return < FeatureProvider value = { flags } > { children } < / FeatureProvider > ;
}
2021-10-20 16:00:11 +00:00
const AppProviders = ( props : AppPropsWithChildren ) = > {
2022-11-10 23:40:01 +00:00
const session = trpc . viewer . public . session . useQuery ( ) . data ;
2022-02-16 15:26:48 +00:00
// No need to have intercom on public pages - Good for Page Performance
const isPublicPage = usePublicPage ( ) ;
2022-07-28 19:58:26 +00:00
2022-02-16 15:26:48 +00:00
const RemainingProviders = (
2022-07-28 19:58:26 +00:00
< EventCollectionProvider options = { { apiPath : "/api/collect-events" } } >
2022-09-02 23:55:12 +00:00
< SessionProvider session = { session || undefined } >
< CustomI18nextProvider { ...props } >
< TooltipProvider >
2023-04-05 18:14:46 +00:00
< CalcomThemeProvider
2023-02-06 22:50:08 +00:00
nonce = { props . pageProps . nonce }
2023-04-17 12:16:54 +00:00
isThemeSupported = { props . Component . isThemeSupported }
isBookingPage = { props . Component . isBookingPage } >
2023-03-25 00:59:04 +00:00
< FeatureFlagsProvider >
< MetaProvider > { props . children } < / MetaProvider >
< / FeatureFlagsProvider >
2023-04-05 18:14:46 +00:00
< / CalcomThemeProvider >
2022-09-02 23:55:12 +00:00
< / TooltipProvider >
< / CustomI18nextProvider >
< / SessionProvider >
2022-07-28 19:58:26 +00:00
< / EventCollectionProvider >
2022-02-16 15:26:48 +00:00
) ;
2022-06-02 16:19:01 +00:00
if ( isPublicPage ) {
return RemainingProviders ;
}
2021-08-27 12:11:24 +00:00
return (
2022-06-02 16:19:01 +00:00
< DynamicHelpscoutProvider >
< DynamicIntercomProvider > { RemainingProviders } < / DynamicIntercomProvider >
< / DynamicHelpscoutProvider >
2021-08-27 12:11:24 +00:00
) ;
} ;
2021-09-30 12:28:30 +00:00
export default AppProviders ;