perf: Speed up User Type booking page (#20516)
* refactor [user]/[type] server logic * refactor useSlot * refactor * fix * fix OG images * fix
This commit is contained in:
1 parent
3b945ba439
commit
19a0f968ca
7 files changed
+53
-78
No files matched your search
@@ -25,7 +25,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
title,
|
||||
profile: { name: profileName, image: profileImage },
|
||||
users: [
|
||||
...(eventData?.users || []).map((user) => ({
|
||||
...(eventData?.subsetOfUsers || []).map((user) => ({
|
||||
name: `${user.name}`,
|
||||
username: `${user.username}`,
|
||||
})),
|
||||
|
||||
@@ -30,7 +30,11 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
||||
title,
|
||||
profile: { name: profileName, image: profileImage },
|
||||
users: [
|
||||
...(eventData?.users || []).map((user) => ({
|
||||
...(
|
||||
(eventData as UserTypePageProps["eventData"])?.subsetOfUsers ??
|
||||
(eventData as TeamTypePageProps["eventData"])?.users ??
|
||||
[]
|
||||
).map((user) => ({
|
||||
name: `${user.name}`,
|
||||
username: `${user.username}`,
|
||||
})),
|
||||
|
||||
@@ -35,6 +35,7 @@ function Type({ slug, user, isEmbed, booking, isBrandingHidden, eventData, orgBa
|
||||
eventSlug={slug}
|
||||
bookingData={booking}
|
||||
hideBranding={isBrandingHidden}
|
||||
eventData={eventData}
|
||||
entity={{ ...eventData.entity, eventTypeId: eventData?.id }}
|
||||
durationConfig={eventData.metadata?.multipleDuration}
|
||||
orgBannerUrl={orgBannerUrl}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createRouterCaller } from "app/_trpc/context";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import type { Session } from "next-auth";
|
||||
import { z } from "zod";
|
||||
@@ -9,33 +8,16 @@ import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import type { getPublicEvent } from "@calcom/features/eventtypes/lib/getPublicEvent";
|
||||
import { getUsernameList } from "@calcom/lib/defaultEvents";
|
||||
import { EventRepository } from "@calcom/lib/server/repository/event";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { RedirectType } from "@calcom/prisma/client";
|
||||
import { publicViewerRouter } from "@calcom/trpc/server/routers/publicViewer/_router";
|
||||
|
||||
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
|
||||
|
||||
type Props = {
|
||||
eventData: Omit<
|
||||
Pick<
|
||||
NonNullable<Awaited<ReturnType<typeof getPublicEvent>>>,
|
||||
"id" | "length" | "metadata" | "entity" | "profile" | "title" | "subsetOfUsers" | "hidden"
|
||||
>,
|
||||
"profile" | "subsetOfUsers"
|
||||
> & {
|
||||
profile: {
|
||||
image: string | undefined;
|
||||
name: string | null;
|
||||
username: string | null;
|
||||
};
|
||||
users: {
|
||||
username: string;
|
||||
name: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
eventData: NonNullable<Awaited<ReturnType<typeof getPublicEvent>>>;
|
||||
booking?: GetBookingType;
|
||||
rescheduleUid: string | null;
|
||||
bookingUid: string | null;
|
||||
@@ -144,13 +126,16 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
|
||||
|
||||
// We use this to both prefetch the query on the server,
|
||||
// as well as to check if the event exist, so we c an show a 404 otherwise.
|
||||
const caller = await createRouterCaller(publicViewerRouter);
|
||||
const eventData = await caller.event({
|
||||
username: usernames.join("+"),
|
||||
eventSlug: slug,
|
||||
org,
|
||||
fromRedirectOfNonOrgLink: context.query.orgRedirection === "true",
|
||||
});
|
||||
|
||||
const eventData = await EventRepository.getPublicEvent(
|
||||
{
|
||||
username: usernames.join("+"),
|
||||
eventSlug: slug,
|
||||
org,
|
||||
fromRedirectOfNonOrgLink: context.query.orgRedirection === "true",
|
||||
},
|
||||
session?.user?.id
|
||||
);
|
||||
|
||||
if (!eventData) {
|
||||
return {
|
||||
@@ -160,24 +145,11 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
|
||||
|
||||
const props: Props = {
|
||||
eventData: {
|
||||
id: eventData.id,
|
||||
entity: eventData.entity,
|
||||
length: eventData.length,
|
||||
...eventData,
|
||||
metadata: {
|
||||
...eventData.metadata,
|
||||
multipleDuration: [15, 30, 45, 60, 90],
|
||||
},
|
||||
profile: {
|
||||
image: eventData.profile.image,
|
||||
name: eventData.profile.name ?? null,
|
||||
username: eventData.profile.username ?? null,
|
||||
},
|
||||
title: eventData.title,
|
||||
users: eventData.subsetOfUsers.map((user) => ({
|
||||
username: user.username ?? "",
|
||||
name: user.name ?? "",
|
||||
})),
|
||||
hidden: eventData.hidden,
|
||||
},
|
||||
user: usernames.join("+"),
|
||||
slug,
|
||||
@@ -238,13 +210,15 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
|
||||
const org = isValidOrgDomain ? currentOrgDomain : null;
|
||||
// We use this to both prefetch the query on the server,
|
||||
// as well as to check if the event exist, so we can show a 404 otherwise.
|
||||
const caller = await createRouterCaller(publicViewerRouter);
|
||||
const eventData = await caller.event({
|
||||
username,
|
||||
eventSlug: slug,
|
||||
org,
|
||||
fromRedirectOfNonOrgLink: context.query.orgRedirection === "true",
|
||||
});
|
||||
const eventData = await EventRepository.getPublicEvent(
|
||||
{
|
||||
username,
|
||||
eventSlug: slug,
|
||||
org,
|
||||
fromRedirectOfNonOrgLink: context.query.orgRedirection === "true",
|
||||
},
|
||||
session?.user?.id
|
||||
);
|
||||
|
||||
if (!eventData) {
|
||||
return {
|
||||
@@ -259,23 +233,7 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
|
||||
: user?.allowSEOIndexing;
|
||||
|
||||
const props: Props = {
|
||||
eventData: {
|
||||
id: eventData.id,
|
||||
entity: eventData.entity,
|
||||
length: eventData.length,
|
||||
metadata: eventData.metadata,
|
||||
profile: {
|
||||
image: eventData.profile.image,
|
||||
name: eventData.profile.name ?? null,
|
||||
username: eventData.profile.username ?? null,
|
||||
},
|
||||
title: eventData.title,
|
||||
users: eventData.subsetOfUsers.map((user) => ({
|
||||
username: user.username ?? "",
|
||||
name: user.name ?? "",
|
||||
})),
|
||||
hidden: eventData.hidden,
|
||||
},
|
||||
eventData: eventData,
|
||||
user: username,
|
||||
slug,
|
||||
isBrandingHidden: user?.hideBranding,
|
||||
|
||||
@@ -5,7 +5,6 @@ import dayjs from "@calcom/dayjs";
|
||||
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { useSlotReservationId } from "@calcom/features/bookings/Booker/useSlotReservationId";
|
||||
import { isBookingDryRun } from "@calcom/features/bookings/Booker/utils/isBookingDryRun";
|
||||
import type { BookerEvent } from "@calcom/features/bookings/types";
|
||||
import {
|
||||
MINUTES_TO_BOOK,
|
||||
PUBLIC_QUERY_RESERVATION_INTERVAL_SECONDS,
|
||||
@@ -78,7 +77,7 @@ const useQuickAvailabilityChecks = ({
|
||||
|
||||
export type UseSlotsReturnType = ReturnType<typeof useSlots>;
|
||||
|
||||
export const useSlots = (event: { data?: Pick<BookerEvent, "id" | "length"> | null }) => {
|
||||
export const useSlots = (event: { id: number; length: number } | null) => {
|
||||
const selectedDuration = useBookerStore((state) => state.selectedDuration);
|
||||
const searchParams = useCompatSearchParams();
|
||||
const [selectedTimeslot, setSelectedTimeslot, tentativeSelectedTimeslots, setTentativeSelectedTimeslots] =
|
||||
@@ -107,13 +106,13 @@ export const useSlots = (event: { data?: Pick<BookerEvent, "id" | "length"> | nu
|
||||
});
|
||||
|
||||
const handleRemoveSlot = () => {
|
||||
if (event?.data) {
|
||||
if (event?.id && slotReservationId) {
|
||||
removeSelectedSlot.mutate({ uid: slotReservationId });
|
||||
}
|
||||
};
|
||||
|
||||
const eventTypeId = event.data?.id;
|
||||
const eventDuration = selectedDuration || event.data?.length || 0;
|
||||
const eventTypeId = event?.id;
|
||||
const eventDuration = selectedDuration || event?.length || 0;
|
||||
const allSelectedTimeslots = [...tentativeSelectedTimeslots, selectedTimeslot].filter(
|
||||
(slot): slot is string => slot !== null
|
||||
);
|
||||
@@ -153,7 +152,7 @@ export const useSlots = (event: { data?: Pick<BookerEvent, "id" | "length"> | nu
|
||||
clearInterval(interval);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [event?.data?.id, timeSlotToBeBooked]);
|
||||
}, [event?.id, timeSlotToBeBooked]);
|
||||
|
||||
return {
|
||||
setSelectedTimeslot,
|
||||
|
||||
@@ -18,7 +18,7 @@ export type useScheduleForEventReturnType = ReturnType<typeof useScheduleForEven
|
||||
* Using this hook means you only need to use one hook, instead
|
||||
* of combining multiple conditional hooks.
|
||||
*/
|
||||
export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean }) => {
|
||||
export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean; disabled?: boolean }) => {
|
||||
const [username, eventSlug, isTeamEvent, org] = useBookerStore(
|
||||
(state) => [state.username, state.eventSlug, state.isTeamEvent, state.org],
|
||||
shallow
|
||||
@@ -34,7 +34,7 @@ export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean }) => {
|
||||
},
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: Boolean(username) && Boolean(eventSlug),
|
||||
enabled: !props?.disabled && Boolean(username) && Boolean(eventSlug),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -22,19 +22,32 @@ import { useBookerStore, useInitializeBookerStore } from "@calcom/features/booki
|
||||
import { useEvent, useScheduleForEvent } from "@calcom/features/bookings/Booker/utils/event";
|
||||
import { getLastBookingResponse } from "@calcom/features/bookings/Booker/utils/lastBookingResponse";
|
||||
import { useBrandColors } from "@calcom/features/bookings/Booker/utils/use-brand-colors";
|
||||
import type { getPublicEvent } from "@calcom/features/eventtypes/lib/getPublicEvent";
|
||||
import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
import { BookerLayouts } from "@calcom/prisma/zod-utils";
|
||||
|
||||
type BookerWebWrapperAtomProps = BookerProps;
|
||||
type BookerWebWrapperAtomProps = BookerProps & {
|
||||
eventData?: NonNullable<Awaited<ReturnType<typeof getPublicEvent>>>;
|
||||
};
|
||||
|
||||
export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const event = useEvent({
|
||||
const clientFetchedEvent = useEvent({
|
||||
disabled: !!props.eventData,
|
||||
fromRedirectOfNonOrgLink: props.entity.fromRedirectOfNonOrgLink,
|
||||
});
|
||||
const event = props.eventData
|
||||
? {
|
||||
data: props.eventData,
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
}
|
||||
: clientFetchedEvent;
|
||||
|
||||
const bookerLayout = useBookerLayout(event.data);
|
||||
|
||||
const selectedDate = searchParams?.get("date");
|
||||
@@ -110,7 +123,7 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
|
||||
requiresBookerEmailVerification: event?.data?.requiresBookerEmailVerification,
|
||||
onVerifyEmail: bookerForm.beforeVerifyEmail,
|
||||
});
|
||||
const slots = useSlots(event);
|
||||
const slots = useSlots(event?.data ? { id: event.data.id, length: event.data.length } : null);
|
||||
|
||||
const isEmbed = useIsEmbed();
|
||||
|
||||
|
||||
Reference in new issue
Block a user