perf: Avoid unmounting of Shell on navigation and thus reduce number of paints (#10646)
Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
parent
37ce8860b5
commit
a49c34e733
|
@ -3,7 +3,7 @@ import { useRouter } from "next/navigation";
|
|||
import type { ComponentProps } from "react";
|
||||
import React from "react";
|
||||
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { EmptyScreen } from "@calcom/ui";
|
||||
import { AlertCircle } from "@calcom/ui/components/icon";
|
||||
|
@ -12,7 +12,7 @@ type AppsLayoutProps = {
|
|||
children: React.ReactNode;
|
||||
actions?: (className?: string) => JSX.Element;
|
||||
emptyStore?: boolean;
|
||||
} & Omit<ComponentProps<typeof Shell>, "actions">;
|
||||
} & Omit<ComponentProps<typeof ShellMain>, "actions">;
|
||||
|
||||
export default function AppsLayout({ children, actions, emptyStore, ...rest }: AppsLayoutProps) {
|
||||
const { t } = useLocale();
|
||||
|
@ -22,7 +22,7 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
|
|||
|
||||
if (session.status === "loading") return <></>;
|
||||
return (
|
||||
<Shell {...rest} actions={actions?.("block")} hideHeadingOnMobile>
|
||||
<ShellMain {...rest} actions={actions?.("block")} hideHeadingOnMobile>
|
||||
<div className="flex flex-col xl:flex-row">
|
||||
<main className="w-full">
|
||||
{emptyStore ? (
|
||||
|
@ -38,7 +38,6 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
|
|||
)}
|
||||
</main>
|
||||
</div>
|
||||
</Shell>
|
||||
</ShellMain>
|
||||
);
|
||||
}
|
||||
export const getLayout = (page: React.ReactElement) => <AppsLayout>{page}</AppsLayout>;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { usePathname, useSearchParams } from "next/navigation";
|
|||
|
||||
export default function useIsBookingPage() {
|
||||
const pathname = usePathname();
|
||||
const isBookingPage = ["/booking", "/cancel", "/reschedule"].some((route) => pathname?.startsWith(route));
|
||||
const isBookingPage = ["/booking/", "/cancel", "/reschedule"].some((route) => pathname?.startsWith(route));
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
const userParam = searchParams.get("user");
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import React from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
import type { AppProps } from "@lib/app-providers";
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { ChangeEventHandler } from "react";
|
|||
import { useState } from "react";
|
||||
|
||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
||||
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
||||
|
@ -94,6 +95,7 @@ export default function Apps({
|
|||
}
|
||||
|
||||
Apps.PageWrapper = PageWrapper;
|
||||
Apps.getLayout = getLayout;
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const { req, res } = context;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
|
@ -130,15 +131,17 @@ export default function AvailabilityPage() {
|
|||
const { t } = useLocale();
|
||||
return (
|
||||
<div>
|
||||
<Shell
|
||||
<ShellMain
|
||||
heading={t("availability")}
|
||||
hideHeadingOnMobile
|
||||
subtitle={t("configure_availability")}
|
||||
CTA={<NewScheduleButton />}>
|
||||
<WithQuery success={({ data }) => <AvailabilityList {...data} />} customLoader={<SkeletonLoader />} />
|
||||
</Shell>
|
||||
</ShellMain>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
AvailabilityPage.getLayout = getLayout;
|
||||
|
||||
AvailabilityPage.PageWrapper = PageWrapper;
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import type { GetStaticPaths, GetStaticProps } from "next";
|
||||
import { Fragment } from "react";
|
||||
import React from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
|
||||
import BookingLayout from "@calcom/features/bookings/layout/BookingLayout";
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { FiltersContainer } from "@calcom/features/bookings/components/FiltersContainer";
|
||||
import type { filterQuerySchema } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { HorizontalTabs } from "@calcom/ui";
|
||||
import type { VerticalTabItemProps, HorizontalTabItemProps } from "@calcom/ui";
|
||||
import { Alert, Button, EmptyScreen } from "@calcom/ui";
|
||||
import { Calendar } from "@calcom/ui/components/icon";
|
||||
|
||||
|
@ -32,6 +37,28 @@ type RecurringInfo = {
|
|||
bookings: { [key: string]: Date[] };
|
||||
};
|
||||
|
||||
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
||||
{
|
||||
name: "upcoming",
|
||||
href: "/bookings/upcoming",
|
||||
},
|
||||
{
|
||||
name: "unconfirmed",
|
||||
href: "/bookings/unconfirmed",
|
||||
},
|
||||
{
|
||||
name: "recurring",
|
||||
href: "/bookings/recurring",
|
||||
},
|
||||
{
|
||||
name: "past",
|
||||
href: "/bookings/past",
|
||||
},
|
||||
{
|
||||
name: "cancelled",
|
||||
href: "/bookings/cancelled",
|
||||
},
|
||||
];
|
||||
const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const;
|
||||
|
||||
const descriptionByStatus: Record<NonNullable<BookingListingStatus>, string> = {
|
||||
|
@ -112,7 +139,15 @@ export default function Bookings() {
|
|||
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<BookingLayout heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||
<ShellMain hideHeadingOnMobile heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col flex-wrap lg:flex-row">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
<div className="max-w-full overflow-x-auto xl:ml-auto">
|
||||
<FiltersContainer />
|
||||
</div>
|
||||
</div>
|
||||
<main className="w-full">
|
||||
<div className="flex w-full flex-col" ref={animationParentRef}>
|
||||
{query.status === "error" && (
|
||||
<Alert severity="error" title={t("something_went_wrong")} message={query.error.message} />
|
||||
|
@ -191,11 +226,14 @@ export default function Bookings() {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</BookingLayout>
|
||||
</main>
|
||||
</div>
|
||||
</ShellMain>
|
||||
);
|
||||
}
|
||||
|
||||
Bookings.PageWrapper = PageWrapper;
|
||||
Bookings.getLayout = getLayout;
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (ctx) => {
|
||||
const params = querySchema.safeParse(ctx.params);
|
||||
|
|
|
@ -7,6 +7,7 @@ import type { FC } from "react";
|
|||
import { memo, useEffect, useState } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||
import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
|
||||
import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/embed/EventTypeEmbed";
|
||||
|
@ -15,7 +16,7 @@ import CreateEventTypeDialog from "@calcom/features/eventtypes/components/Create
|
|||
import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog";
|
||||
import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter";
|
||||
import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -932,12 +933,7 @@ const EventTypesPage = () => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeadSeo
|
||||
title="Event Types"
|
||||
description="Create events to share for people to book on your calendar."
|
||||
/>
|
||||
<Shell
|
||||
<ShellMain
|
||||
withoutSeo
|
||||
heading={t("event_types_page_title")}
|
||||
hideHeadingOnMobile
|
||||
|
@ -945,12 +941,17 @@ const EventTypesPage = () => {
|
|||
afterHeading={showProfileBanner && <SetupProfileBanner closeAction={closeBanner} />}
|
||||
beforeCTAactions={<Actions />}
|
||||
CTA={<CTA data={data} />}>
|
||||
<HeadSeo
|
||||
title="Event Types"
|
||||
description="Create events to share for people to book on your calendar."
|
||||
/>
|
||||
<Main data={data} status={status} error={error} filters={filters} />
|
||||
</Shell>
|
||||
</div>
|
||||
</ShellMain>
|
||||
);
|
||||
};
|
||||
|
||||
EventTypesPage.getLayout = getLayout;
|
||||
|
||||
EventTypesPage.PageWrapper = PageWrapper;
|
||||
|
||||
export default EventTypesPage;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
||||
import {
|
||||
AverageEventDurationChart,
|
||||
|
@ -9,7 +10,7 @@ import {
|
|||
} from "@calcom/features/insights/components";
|
||||
import { FiltersProvider } from "@calcom/features/insights/context/FiltersProvider";
|
||||
import { Filters } from "@calcom/features/insights/filters";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { UpgradeTip } from "@calcom/features/tips";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -56,7 +57,7 @@ export default function InsightsPage() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Shell hideHeadingOnMobile>
|
||||
<ShellMain hideHeadingOnMobile>
|
||||
<UpgradeTip
|
||||
title={t("make_informed_decisions")}
|
||||
description={t("make_informed_decisions_description")}
|
||||
|
@ -111,12 +112,13 @@ export default function InsightsPage() {
|
|||
</FiltersProvider>
|
||||
)}
|
||||
</UpgradeTip>
|
||||
</Shell>
|
||||
</ShellMain>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
InsightsPage.PageWrapper = PageWrapper;
|
||||
InsightsPage.getLayout = getLayout;
|
||||
|
||||
// If feature flag is disabled, return not found on getServerSideProps
|
||||
export const getServerSideProps = async () => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { TeamsListing } from "@calcom/features/ee/teams/components";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
@ -17,7 +18,7 @@ function Teams() {
|
|||
const [user] = trpc.viewer.me.useSuspenseQuery();
|
||||
|
||||
return (
|
||||
<Shell
|
||||
<ShellMain
|
||||
heading={t("teams")}
|
||||
hideHeadingOnMobile
|
||||
subtitle={t("create_manage_teams_collaborative")}
|
||||
|
@ -33,7 +34,7 @@ function Teams() {
|
|||
)
|
||||
}>
|
||||
<TeamsListing />
|
||||
</Shell>
|
||||
</ShellMain>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -46,5 +47,5 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
Teams.requiresLicense = false;
|
||||
Teams.PageWrapper = PageWrapper;
|
||||
|
||||
Teams.getLayout = getLayout;
|
||||
export default Teams;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { useRef } from "react";
|
||||
|
||||
/**
|
||||
* Updates in document the number of times a component has been rendered. Helps in 2 ways. Using it doesn't cause any additional renders.
|
||||
* - Did the component render when it shouldn't have?
|
||||
* - Did the component reset its state when it shouldn't have?
|
||||
*/
|
||||
export const RenderCounter = ({ label }: { label: string }) => {
|
||||
const counterRef = useRef(0);
|
||||
counterRef.current++;
|
||||
return (
|
||||
<span>
|
||||
<span>{label}:</span>
|
||||
<span>{counterRef.current} </span>
|
||||
</span>
|
||||
);
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export { RenderCounter } from "./components/RenderCounter";
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@calcom/debugging",
|
||||
"description": "Debugging utilities",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "./index.ts"
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"extends": "@calcom/tsconfig/react-library.json",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"../../apps/web/next-env.d.ts",
|
||||
"../types/*.d.ts",
|
||||
"../types/next-auth.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import type { ComponentProps } from "react";
|
||||
import React from "react";
|
||||
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
|
||||
export default function MainLayout({
|
||||
children,
|
||||
...rest
|
||||
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
||||
return (
|
||||
<Shell withoutMain={true} {...rest}>
|
||||
{children}
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
export const getLayout = (page: React.ReactElement) => <MainLayout>{page}</MainLayout>;
|
|
@ -1,51 +0,0 @@
|
|||
import type { ComponentProps } from "react";
|
||||
import React from "react";
|
||||
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { HorizontalTabs } from "@calcom/ui";
|
||||
import type { VerticalTabItemProps, HorizontalTabItemProps } from "@calcom/ui";
|
||||
|
||||
import { FiltersContainer } from "../components/FiltersContainer";
|
||||
|
||||
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
||||
{
|
||||
name: "upcoming",
|
||||
href: "/bookings/upcoming",
|
||||
},
|
||||
{
|
||||
name: "unconfirmed",
|
||||
href: "/bookings/unconfirmed",
|
||||
},
|
||||
{
|
||||
name: "recurring",
|
||||
href: "/bookings/recurring",
|
||||
},
|
||||
{
|
||||
name: "past",
|
||||
href: "/bookings/past",
|
||||
},
|
||||
{
|
||||
name: "cancelled",
|
||||
href: "/bookings/cancelled",
|
||||
},
|
||||
];
|
||||
|
||||
export default function BookingLayout({
|
||||
children,
|
||||
...rest
|
||||
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
||||
return (
|
||||
<Shell {...rest} hideHeadingOnMobile>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col flex-wrap lg:flex-row">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
<div className="max-w-full overflow-x-auto xl:ml-auto">
|
||||
<FiltersContainer />
|
||||
</div>
|
||||
</div>
|
||||
<main className="w-full">{children}</main>
|
||||
</div>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
export const getLayout = (page: React.ReactElement) => <BookingLayout>{page}</BookingLayout>;
|
|
@ -3,7 +3,8 @@ import { useRouter } from "next/navigation";
|
|||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
import { ShellMain } from "@calcom/features/shell/Shell";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -49,7 +50,7 @@ function WorkflowsPage() {
|
|||
});
|
||||
|
||||
return (
|
||||
<Shell
|
||||
<ShellMain
|
||||
heading={t("workflows")}
|
||||
title={t("workflows")}
|
||||
subtitle={t("workflows_to_automate_notifications")}
|
||||
|
@ -92,7 +93,7 @@ function WorkflowsPage() {
|
|||
</FilterResults>
|
||||
</>
|
||||
</LicenseRequired>
|
||||
</Shell>
|
||||
</ShellMain>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -221,4 +222,6 @@ const Filter = (props: {
|
|||
);
|
||||
};
|
||||
|
||||
WorkflowsPage.getLayout = getLayout;
|
||||
|
||||
export default WorkflowsPage;
|
||||
|
|
|
@ -4026,6 +4026,12 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/debugging@workspace:packages/debugging":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/debugging@workspace:packages/debugging"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/discord@workspace:packages/app-store/discord":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/discord@workspace:packages/app-store/discord"
|
||||
|
|
Loading…
Reference in New Issue
Block a user