diff --git a/apps/web/app/_trpc/trpc-provider.tsx b/apps/web/app/_trpc/trpc-provider.tsx index 6bb9e77a71..cc339e9cec 100644 --- a/apps/web/app/_trpc/trpc-provider.tsx +++ b/apps/web/app/_trpc/trpc-provider.tsx @@ -1,14 +1,18 @@ -import { type DehydratedState, QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { HydrateClient } from "app/_trpc/HydrateClient"; +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { trpc } from "app/_trpc/client"; import { useState } from "react"; import superjson from "superjson"; -import { httpBatchLink, httpLink, loggerLink, splitLink } from "@calcom/trpc/client"; +import { httpBatchLink, httpLink, loggerLink, splitLink, TRPCClientError } from "@calcom/trpc/client"; import { ENDPOINTS } from "@calcom/trpc/react/shared"; +import type { AppRouter } from "@calcom/trpc/server/routers/_app"; export type Endpoint = (typeof ENDPOINTS)[number]; +const MAX_QUERY_RETRIES = 3; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const resolveEndpoint = (links: any) => { // TODO: Update our trpc routes so they are more clear. @@ -32,16 +36,44 @@ const resolveEndpoint = (links: any) => { }; }; -export const TrpcProvider: React.FC<{ children: React.ReactNode; dehydratedState?: DehydratedState }> = ({ - children, - dehydratedState, -}) => { +const isTRPCClientError = (cause: unknown): cause is TRPCClientError => { + return cause instanceof TRPCClientError; +}; + +type Props = { + children: React.ReactNode; +}; + +export const TrpcProvider = ({ children }: Props) => { const [queryClient] = useState( () => new QueryClient({ - defaultOptions: { queries: { staleTime: 5000 } }, + // these configurations are copied from "packages/trpc/react/trpc.ts" + defaultOptions: { + queries: { + /** + * 1s should be enough to just keep identical query waterfalls low + * @example if one page components uses a query that is also used further down the tree + */ + staleTime: 1000, + /** + * Retry `useQuery()` calls depending on this function + */ + retry(failureCount, error) { + if (isTRPCClientError(error) && error.data) { + const { code } = error.data; + if (code === "BAD_REQUEST" || code === "FORBIDDEN" || code === "UNAUTHORIZED") { + // if input data is wrong or you're not authorized there's no point retrying a query + return false; + } + } + return failureCount < MAX_QUERY_RETRIES; + }, + }, + }, }) ); + const url = typeof window !== "undefined" ? "/api/trpc" @@ -94,9 +126,7 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode; dehydratedState return ( - - {dehydratedState ? {children} : children} - + {children} ); }; diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index e900cbb6b2..9b69346a2e 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,3 +1,4 @@ +import { TrpcProvider } from "app/_trpc/trpc-provider"; import { dir } from "i18next"; import { Inter } from "next/font/google"; import localFont from "next/font/local"; @@ -112,7 +113,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo }} /> )} - {children} + {children} ); diff --git a/apps/web/lib/app-providers-app-dir.tsx b/apps/web/lib/app-providers-app-dir.tsx index 28c60de233..58abc62da0 100644 --- a/apps/web/lib/app-providers-app-dir.tsx +++ b/apps/web/lib/app-providers-app-dir.tsx @@ -1,5 +1,5 @@ import { TooltipProvider } from "@radix-ui/react-tooltip"; -import { TrpcProvider } from "app/_trpc/trpc-provider"; +import { HydrateClient } from "app/_trpc/HydrateClient"; import { dir } from "i18next"; import type { Session } from "next-auth"; import { SessionProvider, useSession } from "next-auth/react"; @@ -265,43 +265,45 @@ function OrgBrandProvider({ children }: { children: React.ReactNode }) { const AppProviders = (props: PageWrapperProps) => { // No need to have intercom on public pages - Good for Page Performance const isBookingPage = useIsBookingPage(); - 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 */} - - - - {/* @ts-expect-error FIXME remove this comment when upgrading typescript to v5 */} - - {props.children} - - - - - - - - - + + + + + {/* 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 */} + + + + {/* @ts-expect-error FIXME remove this comment when upgrading typescript to v5 */} + + {props.children} + + + + + + + + + ); + const Hydrated = props.dehydratedState ? ( + {RemainingProviders} + ) : ( + RemainingProviders ); if (isBookingPage) { - return RemainingProviders; + return Hydrated; } return ( - {RemainingProviders} + {Hydrated} );