fix i18n flicker on booking pages (#1013)

This commit is contained in:
Alex Johansson 2021-10-20 18:00:11 +02:00 committed by GitHub
parent b8e8319b23
commit c28d800aa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 44 deletions

View File

@ -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<typeof I18nextAdapter>;
return <I18nextAdapter {...passedProps} />;
};
const AppProviders = (props: AppProps) => {
const AppProviders = (props: AppPropsWithChildren) => {
const session = trpc.useQuery(["viewer.session"]).data;
return (
<TelemetryProvider value={createTelemetryClient()}>
<IdProvider>
<DynamicIntercomProvider>
<Provider session={session || undefined}>
<CustomI18nextProvider>{props.children}</CustomI18nextProvider>
<CustomI18nextProvider {...props}>{props.children}</CustomI18nextProvider>
</Provider>
</DynamicIntercomProvider>
</IdProvider>

View File

@ -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<typeof getServerSideProps>) {
const { isReady } = useTheme(props.user.theme);
const { user, eventTypes } = props;
const { t } = useLocale();
const nameOrUsername = user.name || user.username || "";
return (
<>
<HeadSeo
title={user.name || user.username}
description={user.name || user.username}
name={user.name || user.username}
avatar={user.avatar}
title={nameOrUsername}
description={nameOrUsername}
name={nameOrUsername}
avatar={user.avatar || undefined}
/>
{isReady && (
<div className="bg-neutral-50 dark:bg-black h-screen">
@ -31,11 +35,11 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
<div className="mb-8 text-center">
<Avatar
imageSrc={user.avatar}
displayName={user.name}
className="mx-auto w-24 h-24 rounded-full mb-4"
alt={nameOrUsername}
/>
<h1 className="font-cal text-3xl font-bold text-neutral-900 dark:text-white mb-1">
{user.name || user.username}
{nameOrUsername}
</h1>
<p className="text-neutral-500 dark:text-white">{user.bio}</p>
</div>
@ -72,6 +76,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
}
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(),
},
};
};

View File

@ -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<typeof getServerSideProps>;
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(),
},
};
};

View File

@ -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(),
},
};
}

View File

@ -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 (

View File

@ -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<Session>; req: NextApiRequest }) {
type CreateContextOptions = trpcNext.CreateNextContextOptions | GetServerSidePropsContext;
async function getUserFromSession({
session,
req,
}: {
session: Maybe<Session>;
req: CreateContextOptions["req"];
}) {
if (!session?.user?.id) {
return null;
}
@ -80,7 +88,7 @@ async function getUserFromSession({ session, req }: { session: Maybe<Session>; 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 });

22
server/lib/ssr.ts Normal file
View File

@ -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;
}

View File

@ -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",
},
});