diff --git a/lib/app-providers.tsx b/lib/app-providers.tsx index 21dd795b42..34d83909e6 100644 --- a/lib/app-providers.tsx +++ b/lib/app-providers.tsx @@ -1,7 +1,7 @@ import { IdProvider } from "@radix-ui/react-id"; import { Provider } from "next-auth/client"; import { appWithTranslation } from "next-i18next"; -import { AppProps } from "next/dist/shared/lib/router/router"; +import type { AppProps as NextAppProps } from "next/app"; import React, { ComponentProps, ReactNode } from "react"; import DynamicIntercomProvider from "@ee/lib/intercom/providerDynamic"; @@ -12,24 +12,38 @@ import { trpc } from "./trpc"; const I18nextAdapter = appWithTranslation(({ children }: { children?: ReactNode }) => <>{children}); -const CustomI18nextProvider = (props: { children: ReactNode }) => { +// Workaround for https://github.com/vercel/next.js/issues/8592 +export type AppProps = NextAppProps & { + /** Will be defined only is there was an error */ + err?: Error; +}; + +type AppPropsWithChildren = AppProps & { + children: ReactNode; +}; + +const CustomI18nextProvider = (props: AppPropsWithChildren) => { const { i18n, locale } = trpc.useQuery(["viewer.i18n"]).data ?? {}; + const passedProps = { ...props, - pageProps: { ...i18n }, - router: { locale }, + pageProps: { + ...props.pageProps, + ...i18n, + }, + router: locale ? { locale } : props.router, } as unknown as ComponentProps; return ; }; -const AppProviders = (props: AppProps) => { +const AppProviders = (props: AppPropsWithChildren) => { const session = trpc.useQuery(["viewer.session"]).data; return ( - {props.children} + {props.children} diff --git a/pages/[user].tsx b/pages/[user].tsx index 1928b0d61e..342e22f6d7 100644 --- a/pages/[user].tsx +++ b/pages/[user].tsx @@ -12,18 +12,22 @@ import EventTypeDescription from "@components/eventtype/EventTypeDescription"; import { HeadSeo } from "@components/seo/head-seo"; import Avatar from "@components/ui/Avatar"; +import { ssrInit } from "@server/lib/ssr"; + export default function User(props: inferSSRProps) { const { isReady } = useTheme(props.user.theme); const { user, eventTypes } = props; const { t } = useLocale(); + const nameOrUsername = user.name || user.username || ""; + return ( <> {isReady && (
@@ -31,11 +35,11 @@ export default function User(props: inferSSRProps) {

- {user.name || user.username} + {nameOrUsername}

{user.bio}

@@ -72,6 +76,8 @@ export default function User(props: inferSSRProps) { } export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const ssr = await ssrInit(context); + const username = (context.query.user as string).toLowerCase(); const user = await prisma.user.findUnique({ @@ -138,6 +144,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => props: { user, eventTypes, + trpcState: ssr.dehydrate(), }, }; }; diff --git a/pages/[user]/[type].tsx b/pages/[user]/[type].tsx index df86c5946c..2b03a97091 100644 --- a/pages/[user]/[type].tsx +++ b/pages/[user]/[type].tsx @@ -7,6 +7,8 @@ import { inferSSRProps } from "@lib/types/inferSSRProps"; import AvailabilityPage from "@components/booking/pages/AvailabilityPage"; +import { ssrInit } from "@server/lib/ssr"; + export type AvailabilityPageProps = inferSSRProps; export default function Type(props: AvailabilityPageProps) { @@ -14,6 +16,7 @@ export default function Type(props: AvailabilityPageProps) { } export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const ssr = await ssrInit(context); // get query params and typecast them to string // (would be even better to assert them instead of typecasting) const userParam = asStringOrNull(context.query.user); @@ -185,6 +188,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => date: dateParam, eventType: eventTypeObject, workingHours, + trpcState: ssr.dehydrate(), }, }; }; diff --git a/pages/[user]/book.tsx b/pages/[user]/book.tsx index e9709e32e9..304f73fd94 100644 --- a/pages/[user]/book.tsx +++ b/pages/[user]/book.tsx @@ -9,6 +9,8 @@ import { inferSSRProps } from "@lib/types/inferSSRProps"; import BookingPage from "@components/booking/pages/BookingPage"; +import { ssrInit } from "@server/lib/ssr"; + dayjs.extend(utc); dayjs.extend(timezone); @@ -19,6 +21,8 @@ export default function Book(props: BookPageProps) { } export async function getServerSideProps(context: GetServerSidePropsContext) { + const ssr = await ssrInit(context); + const user = await prisma.user.findUnique({ where: { username: asStringOrThrow(context.query.user), @@ -107,6 +111,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { }, eventType: eventTypeObject, booking, + trpcState: ssr.dehydrate(), }, }; } diff --git a/pages/_app.tsx b/pages/_app.tsx index 3f412e5c46..c1f8ae3ca3 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,8 +1,7 @@ import { DefaultSeo } from "next-seo"; -import type { AppProps as NextAppProps } from "next/app"; import superjson from "superjson"; -import AppProviders from "@lib/app-providers"; +import AppProviders, { AppProps } from "@lib/app-providers"; import { seoConfig } from "@lib/config/next-seo.config"; import I18nLanguageHandler from "@components/I18nLanguageHandler"; @@ -16,12 +15,6 @@ import { Maybe } from "@trpc/server"; import "../styles/globals.css"; -// Workaround for https://github.com/vercel/next.js/issues/8592 -export type AppProps = NextAppProps & { - /** Will be defined only is there was an error */ - err?: Error; -}; - function MyApp(props: AppProps) { const { Component, pageProps, err } = props; return ( diff --git a/server/createContext.ts b/server/createContext.ts index 89131a2f06..ed27e3b2d1 100644 --- a/server/createContext.ts +++ b/server/createContext.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { NextApiRequest } from "next"; +import { GetServerSidePropsContext, NextApiRequest } from "next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { getSession, Session } from "@lib/auth"; @@ -11,7 +11,15 @@ import * as trpc from "@trpc/server"; import { Maybe } from "@trpc/server"; import * as trpcNext from "@trpc/server/adapters/next"; -async function getUserFromSession({ session, req }: { session: Maybe; req: NextApiRequest }) { +type CreateContextOptions = trpcNext.CreateNextContextOptions | GetServerSidePropsContext; + +async function getUserFromSession({ + session, + req, +}: { + session: Maybe; + req: CreateContextOptions["req"]; +}) { if (!session?.user?.id) { return null; } @@ -80,7 +88,7 @@ async function getUserFromSession({ session, req }: { session: Maybe; r * Creates context for an incoming request * @link https://trpc.io/docs/context */ -export const createContext = async ({ req, res }: trpcNext.CreateNextContextOptions) => { +export const createContext = async ({ req, res }: CreateContextOptions) => { // for API-response caching see https://trpc.io/docs/caching const session = await getSession({ req }); diff --git a/server/lib/ssr.ts b/server/lib/ssr.ts new file mode 100644 index 0000000000..076c5b8d05 --- /dev/null +++ b/server/lib/ssr.ts @@ -0,0 +1,22 @@ +import { GetServerSidePropsContext } from "next"; +import superjson from "superjson"; + +import { createContext } from "@server/createContext"; +import { createSSGHelpers } from "@trpc/react/ssg"; + +import { appRouter } from "../routers/_app"; + +export async function ssrInit(context: GetServerSidePropsContext) { + const ctx = await createContext(context); + + const ssr = createSSGHelpers({ + router: appRouter, + transformer: superjson, + ctx, + }); + + // always preload "viewer.i18n" + await ssr.fetchQuery("viewer.i18n"); + + return ssr; +} diff --git a/server/ssg.ts b/server/ssg.ts deleted file mode 100644 index fa1bfd738e..0000000000 --- a/server/ssg.ts +++ /dev/null @@ -1,21 +0,0 @@ -import prisma from "@lib/prisma"; - -import { createSSGHelpers } from "@trpc/react/ssg"; - -import { appRouter } from "./routers/_app"; - -export const ssg = createSSGHelpers({ - router: appRouter, - ctx: { - prisma, - session: null, - user: null, - i18n: { - _nextI18Next: { - initialI18nStore: null, - userConfig: null, - }, - }, - locale: "en", - }, -});