chore: [app-router-migration-8] Migrate all pages in /video directory (#12623)
* chore: migrate trpc, ssgInit, ssrInit * intuita codemod: remove-get-static-props * intuita codemod: app-directory-boilerplate-calcom * manual: refactor and add title/description metadata * intuita codemod: app-directory-boilerplate-calcom * manual: refactor and add metadata * manual: add ab test redirect * fix: restore emails * fix linter and ts issues --------- Co-authored-by: Dmytro Hryshyn <dev.dmytroh@gmail.com> Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
15 files changed
+388
-1
No files matched your search
@@ -315,3 +315,4 @@ APP_ROUTER_APPS_CATEGORIES_ENABLED=0
|
||||
# whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not
|
||||
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0
|
||||
APP_ROUTER_BOOKINGS_STATUS_ENABLED=0
|
||||
APP_ROUTER_VIDEO_ENABLED=0
|
||||
@@ -12,6 +12,7 @@ const ROUTES: [URLPattern, boolean][] = [
|
||||
["/apps/categories", process.env.APP_ROUTER_APPS_CATEGORIES_ENABLED === "1"] as const,
|
||||
["/apps/categories/:category", process.env.APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED === "1"] as const,
|
||||
["/bookings/:status", process.env.APP_ROUTER_BOOKINGS_STATUS_ENABLED === "1"] as const,
|
||||
["/video/:path*", process.env.APP_ROUTER_VIDEO_ENABLED === "1"] as const,
|
||||
].map(([pathname, enabled]) => [
|
||||
new URLPattern({
|
||||
pathname,
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import OldPage from "@pages/teams/index";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { type Params } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import PageWrapper from "@components/PageWrapperAppDir";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("teams"),
|
||||
(t) => t("create_manage_teams_collaborative")
|
||||
);
|
||||
|
||||
type PageProps = {
|
||||
params: Params;
|
||||
};
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const ssr = await ssrInit();
|
||||
await ssr.viewer.me.prefetch();
|
||||
|
||||
const session = await getServerSession({
|
||||
req: context.req,
|
||||
});
|
||||
|
||||
if (!session) {
|
||||
const token = Array.isArray(context.query.token) ? context.query.token[0] : context.query.token;
|
||||
|
||||
const callbackUrl = token ? `/teams?token=${encodeURIComponent(token)}` : null;
|
||||
return redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
||||
}
|
||||
|
||||
return { dehydratedState: await ssr.dehydrate() };
|
||||
}
|
||||
|
||||
const Page = async ({ params }: PageProps) => {
|
||||
const h = headers();
|
||||
const nonce = h.get("x-nonce") ?? undefined;
|
||||
|
||||
const legacyCtx = buildLegacyCtx(h, cookies(), params);
|
||||
// @ts-expect-error `req` of type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to `req` in `GetServerSidePropsContext`
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
return (
|
||||
<PageWrapper
|
||||
getLayout={getLayout}
|
||||
requiresLicense={false}
|
||||
nonce={nonce}
|
||||
themeBasis={null}
|
||||
dehydratedState={props.dehydratedState}>
|
||||
<OldPage />
|
||||
</PageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,130 @@
|
||||
import OldPage from "@pages/video/[uid]";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { type Params } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import PageWrapper from "@components/PageWrapperAppDir";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
() => `${APP_NAME} Video`,
|
||||
(t) => t("quick_video_meeting")
|
||||
);
|
||||
|
||||
type PageProps = Readonly<{
|
||||
params: Params;
|
||||
}>;
|
||||
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const ssr = await ssrInit();
|
||||
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: context.query.uid as string,
|
||||
},
|
||||
select: {
|
||||
...bookingMinimalSelect,
|
||||
uid: true,
|
||||
description: true,
|
||||
isRecorded: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
timeZone: true,
|
||||
name: true,
|
||||
email: true,
|
||||
organization: {
|
||||
select: {
|
||||
calVideoLogo: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
references: {
|
||||
select: {
|
||||
uid: true,
|
||||
type: true,
|
||||
meetingUrl: true,
|
||||
meetingPassword: true,
|
||||
},
|
||||
where: {
|
||||
type: "daily_video",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!booking || booking.references.length === 0 || !booking.references[0].meetingUrl) {
|
||||
return redirect("/video/no-meeting-found");
|
||||
}
|
||||
|
||||
//daily.co calls have a 60 minute exit buffer when a user enters a call when it's not available it will trigger the modals
|
||||
const now = new Date();
|
||||
const exitDate = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
|
||||
//find out if the meeting is in the past
|
||||
const isPast = booking?.endTime <= exitDate;
|
||||
if (isPast) {
|
||||
return redirect(`/video/meeting-ended/${booking?.uid}`);
|
||||
}
|
||||
|
||||
const bookingObj = Object.assign({}, booking, {
|
||||
startTime: booking.startTime.toString(),
|
||||
endTime: booking.endTime.toString(),
|
||||
});
|
||||
|
||||
const session = await getServerSession({ req: context.req });
|
||||
|
||||
// set meetingPassword to null for guests
|
||||
if (session?.user.id !== bookingObj.user?.id) {
|
||||
bookingObj.references.forEach((bookRef: any) => {
|
||||
bookRef.meetingPassword = null;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
meetingUrl: bookingObj.references[0].meetingUrl ?? "",
|
||||
...(typeof bookingObj.references[0].meetingPassword === "string" && {
|
||||
meetingPassword: bookingObj.references[0].meetingPassword,
|
||||
}),
|
||||
booking: {
|
||||
...bookingObj,
|
||||
...(bookingObj.description && { description: md.render(bookingObj.description) }),
|
||||
},
|
||||
dehydratedState: await ssr.dehydrate(),
|
||||
};
|
||||
}
|
||||
|
||||
const Page = async ({ params }: PageProps) => {
|
||||
const h = headers();
|
||||
const nonce = h.get("x-nonce") ?? undefined;
|
||||
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params);
|
||||
// @ts-expect-error `req` of type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to `req` in `GetServerSidePropsContext`
|
||||
const { dehydratedState, ...restProps } = await getData(legacyCtx);
|
||||
|
||||
return (
|
||||
<PageWrapper
|
||||
getLayout={null}
|
||||
requiresLicense={false}
|
||||
nonce={nonce}
|
||||
themeBasis={null}
|
||||
dehydratedState={dehydratedState}>
|
||||
<OldPage {...restProps} />
|
||||
</PageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,76 @@
|
||||
import OldPage from "@pages/video/meeting-ended/[uid]";
|
||||
import { type Params } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import PageWrapper from "@components/PageWrapperAppDir";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
() => "Meeting Unavailable",
|
||||
() => "Meeting Unavailable"
|
||||
);
|
||||
|
||||
type PageProps = Readonly<{
|
||||
params: Params;
|
||||
}>;
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: typeof context?.params?.uid === "string" ? context.params.uid : "",
|
||||
},
|
||||
select: {
|
||||
...bookingMinimalSelect,
|
||||
uid: true,
|
||||
user: {
|
||||
select: {
|
||||
credentials: true,
|
||||
},
|
||||
},
|
||||
references: {
|
||||
select: {
|
||||
uid: true,
|
||||
type: true,
|
||||
meetingUrl: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!booking) {
|
||||
return redirect("/video/no-meeting-found");
|
||||
}
|
||||
|
||||
const bookingObj = Object.assign({}, booking, {
|
||||
startTime: booking.startTime.toString(),
|
||||
endTime: booking.endTime.toString(),
|
||||
});
|
||||
|
||||
return {
|
||||
booking: bookingObj,
|
||||
};
|
||||
}
|
||||
|
||||
const Page = async ({ params }: PageProps) => {
|
||||
const h = headers();
|
||||
const nonce = h.get("x-nonce") ?? undefined;
|
||||
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params);
|
||||
// @ts-expect-error `req` of type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to `req` in `GetServerSidePropsContext`
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
return (
|
||||
<PageWrapper getLayout={null} requiresLicense={false} nonce={nonce} themeBasis={null}>
|
||||
<OldPage {...props} />
|
||||
</PageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import OldPage from "@pages/video/meeting-not-started/[uid]";
|
||||
import { type Params } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import PageWrapper from "@components/PageWrapperAppDir";
|
||||
|
||||
type PageProps = Readonly<{
|
||||
params: Params;
|
||||
}>;
|
||||
|
||||
export const generateMetadata = async ({ params }: PageProps) => {
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: typeof params?.uid === "string" ? params.uid : "",
|
||||
},
|
||||
select: bookingMinimalSelect,
|
||||
});
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => t("this_meeting_has_not_started_yet"),
|
||||
() => booking?.title ?? ""
|
||||
);
|
||||
};
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
uid: typeof context?.params?.uid === "string" ? context.params.uid : "",
|
||||
},
|
||||
select: bookingMinimalSelect,
|
||||
});
|
||||
|
||||
if (!booking) {
|
||||
return redirect("/video/no-meeting-found");
|
||||
}
|
||||
|
||||
const bookingObj = Object.assign({}, booking, {
|
||||
startTime: booking.startTime.toString(),
|
||||
endTime: booking.endTime.toString(),
|
||||
});
|
||||
|
||||
return {
|
||||
booking: bookingObj,
|
||||
};
|
||||
}
|
||||
|
||||
const Page = async ({ params }: PageProps) => {
|
||||
const h = headers();
|
||||
const nonce = h.get("x-nonce") ?? undefined;
|
||||
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params);
|
||||
// @ts-expect-error `req` of type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to `req` in `GetServerSidePropsContext`
|
||||
const props = await getData(legacyCtx);
|
||||
|
||||
return (
|
||||
<PageWrapper getLayout={null} requiresLicense={false} nonce={nonce} themeBasis={null}>
|
||||
<OldPage {...props} />
|
||||
</PageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Page from "@pages/video/no-meeting-found";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
() => "",
|
||||
() => ""
|
||||
);
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { type Params } from "app/_types";
|
||||
import { type ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";
|
||||
import { type ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
|
||||
|
||||
// returns query object same as ctx.query but for app dir
|
||||
export const getQuery = (url: string, params: Params) => {
|
||||
if (!url.length) {
|
||||
return params;
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(url);
|
||||
const searchParamsObj = Object.fromEntries(searchParams.entries());
|
||||
|
||||
return { ...searchParamsObj, ...params };
|
||||
};
|
||||
|
||||
export const buildLegacyCtx = (headers: ReadonlyHeaders, cookies: ReadonlyRequestCookies, params: Params) => {
|
||||
return {
|
||||
query: getQuery(headers.get("x-url") ?? "", params),
|
||||
params,
|
||||
req: { headers, cookies },
|
||||
};
|
||||
};
|
||||
@@ -132,6 +132,8 @@ export const config = {
|
||||
"/future/apps/categories/:category/",
|
||||
"/bookings/:status/",
|
||||
"/future/bookings/:status/",
|
||||
"/video/:path*",
|
||||
"/future/video/:path*",
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayout";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import DailyIframe from "@daily-co/daily-js";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
@@ -19,7 +21,7 @@ import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export type JoinCallPageProps = inferSSRProps<typeof getServerSideProps>;
|
||||
export type JoinCallPageProps = Omit<inferSSRProps<typeof getServerSideProps>, "trpcState">;
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
export default function JoinCall(props: JoinCallPageProps) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import type { NextPageContext } from "next";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import type { NextPageContext } from "next";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, EmptyScreen, HeadSeo } from "@calcom/ui";
|
||||
import { X, ArrowRight } from "@calcom/ui/components/icon";
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
"APP_ROUTER_BOOKINGS_STATUS_ENABLED",
|
||||
"APP_ROUTER_EVENT_TYPES_ENABLED",
|
||||
"APP_ROUTER_SETTINGS_ADMIN_ENABLED",
|
||||
"APP_ROUTER_VIDEO_ENABLED",
|
||||
"APP_USER_NAME",
|
||||
"BASECAMP3_CLIENT_ID",
|
||||
"BASECAMP3_CLIENT_SECRET",
|
||||
|
||||
Reference in new issue
Block a user