* wip * update layoutHOC * wip * remove app router related code no longer used * remove more * await cookies, headers, params, serachparams * update yarn.lock * await cookies, headers, params, serachparams more * update yarn lock again * downgrade next-auth to 4.22.1 * update yarn lock * fix * update yarn lock * fix type checks * update yarn lock * await headers and cookies * restore pages folder * restore yarn.lock * update yarn.lock * await headers and cookies * remove * await params in API routes * updates * restore next.config.js * remove i18n from next.config.js * Fixed tests * Fixed types * Removed duplicate favicon.ico * Fixing more types * ImageResponse moved to next/og * Fixed prisma import issues * dynamic import for @ewsjs/xhr * remove deasync dep * dynamic import for @tryvital/vital-node * fix type checks * add back turbopack command * Type fix * Removed unneeded file * fix turbopack relative path errors * add comments * remove unneeded code * Fixed build errors * await apis * use Promise<Params> type in defaultResponderForAppDir util * refactor scim api route * fix type checks * separate app-routing.config into client-config and server-config * wip * refactor routing forms components * revert unneeded changes for easier review * fix * fix * use CustomTrans * fix type * fix unit tests * fix type error * fix build error * fix build error * fix build error * fix warnings * fix warnings * upgrade @tremor/react and tailwindcss * numCols -> numItems * fix forgot-password e2e test * fix 1 more e2e test * fix login e2e test * fix e2e tests * fix e2e tests * clean up * remove error * use tremor/react 2.11.0 * fix * rename CustomTrans to ServerTrans * address comment * fix test * fix ServerTrans * fix * fix type * fix ServerTrans usages 1 * fix translations * fix type checks * fix type checks * link styling * fix --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
125 lines
3.7 KiB
TypeScript
125 lines
3.7 KiB
TypeScript
import { type TFunction } from "i18next";
|
|
import { cookies, headers } from "next/headers";
|
|
|
|
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
|
import type { AppImageProps, MeetingImageProps } from "@calcom/lib/OgImages";
|
|
import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages";
|
|
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
|
|
import { buildCanonical } from "@calcom/lib/next-seo.config";
|
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
|
import { truncateOnWord } from "@calcom/lib/text";
|
|
|
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
|
|
|
export const getTranslate = async () => {
|
|
const headersList = await headers();
|
|
// If "x-locale" does not exist in header,
|
|
// ensure that config.matcher in middleware includes the page you are testing
|
|
const locale =
|
|
headersList.get("x-locale") ?? (await getLocale(buildLegacyRequest(headersList, await cookies())));
|
|
|
|
return await getTranslation(locale ?? "en", "common");
|
|
};
|
|
|
|
const _generateMetadataWithoutImage = async (
|
|
getTitle: (t: TFunction<string, undefined>) => string,
|
|
getDescription: (t: TFunction<string, undefined>) => string,
|
|
hideBranding?: boolean,
|
|
origin?: string,
|
|
pathname?: string
|
|
) => {
|
|
const h = await headers();
|
|
const _pathname = h.get("x-pathname") ?? pathname ?? "";
|
|
const canonical = buildCanonical({ path: _pathname, origin: origin ?? CAL_URL });
|
|
const t = await getTranslate();
|
|
|
|
const title = getTitle(t);
|
|
const description = getDescription(t);
|
|
const titleSuffix = `| ${APP_NAME}`;
|
|
const displayedTitle = title.includes(titleSuffix) || hideBranding ? title : `${title} ${titleSuffix}`;
|
|
const metadataBase = new URL(IS_CALCOM ? "https://cal.com" : WEBAPP_URL);
|
|
|
|
return {
|
|
title: title.length === 0 ? APP_NAME : displayedTitle,
|
|
description,
|
|
alternates: { canonical },
|
|
openGraph: {
|
|
description: truncateOnWord(description, 158),
|
|
url: canonical,
|
|
type: "website",
|
|
siteName: APP_NAME,
|
|
title: displayedTitle,
|
|
},
|
|
metadataBase,
|
|
};
|
|
};
|
|
|
|
export const _generateMetadata = async (
|
|
getTitle: (t: TFunction<string, undefined>) => string,
|
|
getDescription: (t: TFunction<string, undefined>) => string,
|
|
hideBranding?: boolean,
|
|
origin?: string
|
|
) => {
|
|
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);
|
|
const image =
|
|
SEO_IMG_OGIMG +
|
|
constructGenericImage({
|
|
title: metadata.title,
|
|
description: metadata.description,
|
|
});
|
|
|
|
return {
|
|
...metadata,
|
|
openGraph: {
|
|
...metadata.openGraph,
|
|
images: [image],
|
|
},
|
|
};
|
|
};
|
|
|
|
export const generateMeetingMetadata = async (
|
|
meeting: MeetingImageProps,
|
|
getTitle: (t: TFunction<string, undefined>) => string,
|
|
getDescription: (t: TFunction<string, undefined>) => string,
|
|
hideBranding?: boolean,
|
|
origin?: string,
|
|
pathname?: string
|
|
) => {
|
|
const metadata = await _generateMetadataWithoutImage(
|
|
getTitle,
|
|
getDescription,
|
|
hideBranding,
|
|
origin,
|
|
pathname
|
|
);
|
|
const image = SEO_IMG_OGIMG + constructMeetingImage(meeting);
|
|
|
|
return {
|
|
...metadata,
|
|
openGraph: {
|
|
...metadata.openGraph,
|
|
images: [image],
|
|
},
|
|
};
|
|
};
|
|
|
|
export const generateAppMetadata = async (
|
|
app: AppImageProps,
|
|
getTitle: (t: TFunction<string, undefined>) => string,
|
|
getDescription: (t: TFunction<string, undefined>) => string,
|
|
hideBranding?: boolean,
|
|
origin?: string
|
|
) => {
|
|
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);
|
|
|
|
const image = SEO_IMG_OGIMG + constructAppImage({ ...app, description: metadata.description });
|
|
|
|
return {
|
|
...metadata,
|
|
openGraph: {
|
|
...metadata.openGraph,
|
|
images: [image],
|
|
},
|
|
};
|
|
};
|