From 53e50cd8c7ec481d3ed6b0b1a414a22bcc9af622 Mon Sep 17 00:00:00 2001 From: Luke Vella Date: Tue, 4 Mar 2025 10:45:42 +0000 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Invite=20page=20improvemen?= =?UTF-8?q?ts=20(#1609)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/[locale]/invite/[urlId]/layout.tsx | 32 ----- .../[locale]/invite/[urlId]/logo-color.svg | 10 ++ .../invite/[urlId]/opengraph-image.tsx | 109 ++++++++++++++++++ .../src/app/[locale]/invite/[urlId]/page.tsx | 35 ++++-- 4 files changed, 144 insertions(+), 42 deletions(-) delete mode 100644 apps/web/src/app/[locale]/invite/[urlId]/layout.tsx create mode 100644 apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg create mode 100644 apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx diff --git a/apps/web/src/app/[locale]/invite/[urlId]/layout.tsx b/apps/web/src/app/[locale]/invite/[urlId]/layout.tsx deleted file mode 100644 index 78beaefd9..000000000 --- a/apps/web/src/app/[locale]/invite/[urlId]/layout.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { dehydrate, Hydrate } from "@tanstack/react-query"; -import { notFound } from "next/navigation"; - -import { createSSRHelper } from "@/trpc/server/create-ssr-helper"; - -import Providers from "./providers"; - -export default async function Layout({ - children, - params, -}: { - params: { urlId: string }; - children: React.ReactNode; -}) { - const trpc = await createSSRHelper(); - - const [poll] = await Promise.all([ - trpc.polls.get.fetch({ urlId: params.urlId }), - trpc.polls.participants.list.prefetch({ pollId: params.urlId }), - trpc.polls.comments.list.prefetch({ pollId: params.urlId }), - ]); - - if (!poll || poll.deleted || poll.user?.banned) { - notFound(); - } - - return ( - - {children} - - ); -} diff --git a/apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg b/apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg new file mode 100644 index 000000000..979eacec9 --- /dev/null +++ b/apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx b/apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx new file mode 100644 index 000000000..c8d832b5d --- /dev/null +++ b/apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx @@ -0,0 +1,109 @@ +import { prisma } from "@rallly/database"; +import { ImageResponse } from "next/og"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import * as React from "react"; + +import Logo from "./logo-color.svg"; + +export const contentType = "image/png"; +export const size = { + width: 1200, + height: 630, +}; + +const regularFont = readFile( + join(process.cwd(), "public/static/fonts/inter-regular.ttf"), +); + +const boldFont = readFile( + join(process.cwd(), "public/static/fonts/inter-bold.ttf"), +); + +export default async function OgImage({ + params, +}: { + params: { urlId: string; locale: string }; +}) { + const [regularFontData, boldFontData] = await Promise.all([ + regularFont, + boldFont, + ]); + + const poll = await prisma.poll.findUnique({ + where: { + id: params.urlId as string, + }, + select: { + title: true, + deleted: true, + user: { + select: { + name: true, + banned: true, + }, + }, + }, + }); + + if (!poll || poll.deleted || poll.user?.banned) { + return new Response("Not Found", { status: 404 }); + } + + const title = poll.title; + const author = poll.user?.name; + + return new ImageResponse( + ( +
+
+
+ +
+ Invite +
+
+
+ {author ? ( +
+ By {author} +
+ ) : null} +
+ {title} +
+
+
+
+ ), + { + width: 1200, + height: 630, + fonts: [ + { + name: "Inter", + data: regularFontData, + weight: 400, + }, + { + name: "Inter", + data: boldFontData, + weight: 700, + }, + ], + }, + ); +} diff --git a/apps/web/src/app/[locale]/invite/[urlId]/page.tsx b/apps/web/src/app/[locale]/invite/[urlId]/page.tsx index 25b05c955..926441d03 100644 --- a/apps/web/src/app/[locale]/invite/[urlId]/page.tsx +++ b/apps/web/src/app/[locale]/invite/[urlId]/page.tsx @@ -1,5 +1,6 @@ import { prisma } from "@rallly/database"; import { absoluteUrl } from "@rallly/utils/absolute-url"; +import { dehydrate, Hydrate } from "@tanstack/react-query"; import { notFound } from "next/navigation"; import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page"; @@ -7,6 +8,8 @@ import { PermissionProvider } from "@/contexts/permissions"; import { getTranslation } from "@/i18n/server"; import { createSSRHelper } from "@/trpc/server/create-ssr-helper"; +import Providers from "./providers"; + const PermissionContext = async ({ children, token, @@ -25,15 +28,32 @@ const PermissionContext = async ({ }; export default async function Page({ + params, searchParams, }: { params: { urlId: string }; searchParams: { token: string }; }) { + const trpc = await createSSRHelper(); + + const [poll] = await Promise.all([ + trpc.polls.get.fetch({ urlId: params.urlId }), + trpc.polls.participants.list.prefetch({ pollId: params.urlId }), + trpc.polls.comments.list.prefetch({ pollId: params.urlId }), + ]); + + if (!poll || poll.deleted || poll.user?.banned) { + notFound(); + } + return ( - - - + + + + + + + ); } @@ -53,6 +73,7 @@ export async function generateMetadata({ id: true, title: true, deleted: true, + updatedAt: true, user: { select: { name: true, @@ -77,11 +98,6 @@ export async function generateMetadata({ defaultValue: "Guest", }); - const ogImageUrl = absoluteUrl("/api/og-image-poll", { - title, - author, - }); - return { title, metadataBase: new URL(absoluteUrl()), @@ -91,11 +107,10 @@ export async function generateMetadata({ url: `/invite/${id}`, images: [ { - url: ogImageUrl, + url: `/invite/${id}/opengraph-image?${poll.updatedAt.getTime()}`, width: 1200, height: 630, alt: title, - type: "image/png", }, ], },