♻️ Invite page improvements (#1609)

This commit is contained in:
Luke Vella 2025-03-04 10:45:42 +00:00 committed by GitHub
parent 547a24c6e1
commit 53e50cd8c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 144 additions and 42 deletions

View file

@ -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 (
<Hydrate state={dehydrate(trpc.queryClient)}>
<Providers>{children}</Providers>
</Hydrate>
);
}

View file

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="27" fill="none" viewBox="0 0 120 27">
<g clip-path="url(#a)">
<path fill="#4F46E5" fill-rule="evenodd" d="M4.7 2.842a1.855 1.855 0 0 0-1.858 1.853v17.61c0 1.023.83 1.853 1.857 1.853h17.646a1.855 1.855 0 0 0 1.857-1.853V4.695c0-1.023-.83-1.853-1.857-1.853H4.699Zm17.56 4.844H4.784V22.22H22.26V7.686Zm-2.702-2.063a.928.928 0 0 1-1.597.677.926.926 0 0 1 .669-1.571.93.93 0 0 1 .928.894Zm-12.073.926a.93.93 0 0 0 .963-.927.926.926 0 0 0-.963-.927.93.93 0 0 0-.895.927.926.926 0 0 0 .895.927Zm1.258 8.668a.463.463 0 0 1 0-.655l.657-.655a.463.463 0 0 1 .656 0l2.342 2.337 5.128-5.117a.465.465 0 0 1 .656 0l.657.655a.463.463 0 0 1 0 .656l-6.11 6.095a.465.465 0 0 1-.656 0l-3.33-3.316Zm31.671-8.112h-4.888v12.79h2.54v-4.832h2.428l2.625 4.832h2.727v-.124l-2.968-5.287c.384-.16.73-.353 1.037-.58a3.41 3.41 0 0 0 1.044-1.22c.246-.486.37-1.061.37-1.722 0-.838-.198-1.544-.592-2.117-.394-.574-.958-1.008-1.691-1.3-.733-.292-1.61-.44-2.632-.44Zm-2.348 6.053h2.375c.524 0 .958-.088 1.303-.265a1.79 1.79 0 0 0 .776-.728c.178-.31.268-.665.268-1.063 0-.427-.086-.797-.26-1.107a1.715 1.715 0 0 0-.784-.729c-.351-.17-.795-.255-1.33-.255h-2.348v4.147Zm18.608-6.053h.628l5.052 12.79h-2.696l-1.011-2.846h-5.023l-1.006 2.846H49.93l5.026-12.79h1.72Zm-2.376 8.037h3.672L56.13 9.96l-1.832 5.183Zm21.527 2.856v1.897H67.29V7.105h2.54v10.893h5.996Zm13.782 1.897v-1.898h-5.995V7.107h-2.54v12.788h8.535Zm13.785-1.898v1.898h-8.538V7.105h2.543v10.893l5.995-.001Zm7.371-4.927-2.957-5.965h-2.808l4.481 8.107v4.683h2.577v-4.682l4.47-8.108h-2.788l-2.975 5.965Z" clip-rule="evenodd"/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h115.105v21.316H0z" transform="translate(2.842 2.842)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -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(
(
<div tw="flex relative flex-col bg-gray-100 w-full h-full px-[80px] py-[70px] items-start justify-center">
<div tw="h-full flex flex-col w-full justify-start">
<div tw="flex justify-between items-center w-full">
<Logo height={81} width={370} />
<div tw="flex text-gray-800 text-3xl tracking-tight font-bold">
<span tw="bg-gray-200 px-6 py-3 rounded-full">Invite</span>
</div>
</div>
<div tw="relative flex w-full flex-col mt-auto">
{author ? (
<div
tw="flex text-gray-500 text-[48px] w-[1040px] overflow-hidden"
style={{
width: 1000,
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
By {author}
</div>
) : null}
<div
tw="flex mt-3 text-[64px] font-bold w-[1040px] overflow-hidden"
style={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{title}
</div>
</div>
</div>
</div>
),
{
width: 1200,
height: 630,
fonts: [
{
name: "Inter",
data: regularFontData,
weight: 400,
},
{
name: "Inter",
data: boldFontData,
weight: 700,
},
],
},
);
}

View file

@ -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 (
<PermissionContext token={searchParams.token}>
<InvitePage />
</PermissionContext>
<Hydrate state={dehydrate(trpc.queryClient)}>
<Providers>
<PermissionContext token={searchParams.token}>
<InvitePage />
</PermissionContext>
</Providers>
</Hydrate>
);
}
@ -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",
},
],
},