mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-28 17:56:37 +02:00
♻️ Invite page improvements (#1609)
This commit is contained in:
parent
547a24c6e1
commit
53e50cd8c7
4 changed files with 144 additions and 42 deletions
|
@ -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>
|
||||
);
|
||||
}
|
10
apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg
Normal file
10
apps/web/src/app/[locale]/invite/[urlId]/logo-color.svg
Normal 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 |
109
apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx
Normal file
109
apps/web/src/app/[locale]/invite/[urlId]/opengraph-image.tsx
Normal 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,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
|
@ -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 (
|
||||
<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",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue