Skip to content

Commit 48708f0

Browse files
Add user data to open graph images (#1144)
* Update Open Graph Image with more information * Add Lato fonts for image * Remove unused images * Add background SVGs as components * Update user profile and articles to account for updates * Update requirements because only works on articles * Slip from my last PR (prettier)
1 parent 556ae96 commit 48708f0

File tree

13 files changed

+240
-182
lines changed

13 files changed

+240
-182
lines changed

app/(app)/[username]/page.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,31 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
2323
}
2424

2525
const { bio, name } = profile;
26-
const title = `@${username} ${name ? `(${name}) -` : " -"} Codú`;
27-
28-
const description = `Read writing from ${name}. ${bio}`;
26+
const title = `${name || username} - Codú Profile | Codú - The Web Developer Community`;
27+
const description = `${name || username}'s profile on Codú. ${bio ? `Bio: ${bio}` : "View their posts and contributions."}`;
2928

3029
return {
3130
title,
3231
description,
3332
openGraph: {
33+
title,
3434
description,
35-
type: "article",
36-
images: [`/api/og?title=${encodeURIComponent(`${name} on Codú`)}`],
35+
type: "profile",
36+
images: [
37+
{
38+
url: "/images/og/home-og.png",
39+
width: 1200,
40+
height: 630,
41+
alt: `${name || username}'s profile on Codú`,
42+
},
43+
],
3744
siteName: "Codú",
3845
},
3946
twitter: {
47+
card: "summary_large_image",
48+
title,
4049
description,
41-
images: [`/api/og?title=${encodeURIComponent(`${name} on Codú`)}`],
50+
images: ["/images/og/home-og.png"],
4251
},
4352
};
4453
}
@@ -108,6 +117,9 @@ export default async function Page({
108117
};
109118

110119
return (
111-
<Content profile={shapedProfile} isOwner={isOwner} session={session} />
120+
<>
121+
<h1 className="sr-only">{`${shapedProfile.name || shapedProfile.username}'s Coding Profile`}</h1>
122+
<Content profile={shapedProfile} isOwner={isOwner} session={session} />
123+
</>
112124
);
113125
}

app/(app)/articles/[slug]/page.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
3838
openGraph: {
3939
description: post.excerpt,
4040
type: "article",
41-
images: [`/og?title=${encodeURIComponent(post.title)}`],
41+
images: [
42+
`/og?title=${encodeURIComponent(
43+
post.title,
44+
)}&readTime=${post.readTimeMins}&author=${encodeURIComponent(
45+
post.user.name,
46+
)}&date=${post.updatedAt}`,
47+
],
4248
siteName: "Codú",
4349
},
4450
twitter: {

app/og/route.tsx

Lines changed: 146 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { ImageResponse } from "next/og";
2+
import * as Sentry from "@sentry/nextjs";
3+
import { Stars, Waves } from "@/components/background/background";
4+
25
export const runtime = "edge";
36

47
const height = 630;
@@ -8,153 +11,199 @@ export async function GET(request: Request) {
811
try {
912
const { searchParams } = new URL(request.url);
1013
const origin = `${request.headers.get("x-forwarded-proto") || "http"}://${request.headers.get("host")}`;
11-
// ?title=<title>
12-
const hasTitle = searchParams.has("title");
13-
const title = hasTitle
14-
? searchParams.get("title")?.slice(0, 100)
15-
: "My default title";
1614

17-
const fontData = await fetch(
18-
new URL(
19-
"https://og-playground.vercel.app/inter-latin-ext-700-normal.woff",
20-
import.meta.url,
21-
),
15+
const title = searchParams.get("title");
16+
const author = searchParams.get("author");
17+
const readTime = searchParams.get("readTime");
18+
const date = searchParams.get("date");
19+
20+
if (!title || !author || !readTime || !date) {
21+
throw new Error("Missing required parameters");
22+
}
23+
24+
const regularFontData = await fetch(
25+
new URL("@/assets/Lato-Regular.ttf", import.meta.url),
26+
).then((res) => res.arrayBuffer());
27+
28+
const boldFontData = await fetch(
29+
new URL("@/assets/Lato-Bold.ttf", import.meta.url),
2230
).then((res) => res.arrayBuffer());
31+
2332
return new ImageResponse(
2433
(
2534
<div
26-
tw="flex flex-col h-full w-full justify-center"
35+
tw="flex flex-col h-full w-full"
2736
style={{
28-
padding: "0 114px",
37+
fontFamily: "'Lato'",
2938
backgroundColor: "#1d1b36",
30-
backgroundRepeat: "repeat",
31-
background: `
32-
url('${origin}/images/og/noise.svg'),
33-
radial-gradient(circle at bottom left, rgba(255, 255, 255, 0.1), transparent 40%),
34-
radial-gradient(circle at top right, rgba(255, 255, 255, 0.1), transparent 40%)
35-
`,
39+
backgroundImage: `
40+
url('${origin}/images/og/noise.png'),
41+
radial-gradient(circle at top left, rgba(255, 255, 255, 0.15), transparent 40%),
42+
radial-gradient(circle at top right, rgba(255, 255, 255, 0.15), transparent 40%)
43+
`,
44+
backgroundRepeat: "repeat, no-repeat, no-repeat",
45+
backgroundSize: "100px 100px, 100% 100%, 100% 100%",
3646
}}
3747
>
38-
<div
39-
className="line1"
48+
<Waves
4049
style={{
41-
width: "1200px",
42-
height: "30px",
43-
borderTop: "1px solid #39374E",
44-
borderBottom: "1px solid #39374E",
4550
position: "absolute",
46-
top: "50px",
51+
top: 0,
52+
left: 0,
53+
width,
54+
height,
4755
}}
48-
></div>
49-
<div
50-
className="line2"
56+
/>
57+
<Stars
5158
style={{
52-
width: "1200px",
53-
height: "30px",
54-
borderTop: "1px solid #39374E",
55-
borderBottom: "1px solid #39374E",
5659
position: "absolute",
57-
bottom: "50px",
60+
top: 0,
61+
left: 0,
62+
width,
63+
height,
5864
}}
59-
></div>
65+
/>
6066
<div
61-
className="line3"
6267
style={{
63-
width: "30px",
64-
height: "100%",
65-
borderRight: "1px solid #39374E",
6668
position: "absolute",
67-
left: "50px",
69+
top: "50px",
70+
left: 0,
71+
right: 0,
72+
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
6873
}}
69-
></div>
74+
/>
7075
<div
71-
className="line4"
7276
style={{
73-
width: "30px",
74-
height: "100%",
75-
borderLeft: "1px solid #39374E",
7677
position: "absolute",
77-
right: "50px",
78-
}}
79-
></div>
80-
<img
81-
alt="waves"
82-
src={`${origin}/images/og/waves.svg`}
83-
style={{
84-
position: "absolute",
85-
top: "0",
86-
left: "0",
87-
width: "1200",
88-
height: "630",
78+
bottom: "50px",
79+
left: 0,
80+
right: 0,
81+
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
8982
}}
9083
/>
91-
<img
92-
alt="stars"
93-
src={`${origin}/images/og/stars.svg`}
84+
<div
9485
style={{
9586
position: "absolute",
96-
top: "0",
97-
left: "0",
98-
width: "1200",
99-
height: "630",
87+
left: "50px",
88+
top: 0,
89+
bottom: 0,
90+
width: "40px",
91+
borderLeft: "1px solid rgba(255, 255, 255, 0.1)",
10092
}}
10193
/>
102-
<img
103-
alt="planet"
104-
src={`${origin}/images/og/planet.svg`}
94+
<div
10595
style={{
10696
position: "absolute",
107-
height: "188px",
108-
width: "188px",
109-
right: "0",
110-
top: "10px",
97+
right: "50px",
98+
top: 0,
99+
bottom: 0,
100+
width: "40px",
101+
borderRight: "1px solid rgba(255, 255, 255, 0.1)",
111102
}}
112103
/>
113-
114104
<img
115-
alt="Codu Logo"
116-
style={{
117-
position: "absolute",
118-
height: "53px",
119-
width: "163px",
120-
top: "114px",
121-
left: "114px",
122-
}}
123-
src="https://www.codu.co/_next/image?url=%2Fimages%2Fcodu.png&w=1920&q=75"
105+
alt="planet"
106+
tw="h-[528px] w-[528px] absolute right-[-170px] top-[-170px]"
107+
src={`${origin}/images/og/planet.png`}
124108
/>
125-
<div tw="flex relative flex-col" style={{ marginTop: "200px" }}>
126-
<div
127-
style={{
128-
color: "white",
129-
fontSize: "52px",
130-
lineHeight: 1,
131-
fontWeight: "800",
132-
letterSpacing: "-.025em",
133-
fontFamily: "Lato",
134-
lineClamp: 3,
135-
textWrap: "balance",
136-
}}
137-
>
138-
{title}
109+
{/* Main content */}
110+
<div tw="flex flex-col h-full w-full px-28 py-28">
111+
<div tw="flex flex-grow">
112+
<img
113+
alt="Codu Logo"
114+
tw="h-10"
115+
src={`${origin}/images/codu.png`}
116+
/>
117+
</div>
118+
<div tw="flex flex-col">
119+
<div
120+
tw="mb-8 font-bold"
121+
style={{
122+
color: "white",
123+
fontSize: "46px",
124+
lineHeight: "1.2",
125+
letterSpacing: "-0.025em",
126+
fontFamily: "Lato-Bold",
127+
display: "-webkit-box",
128+
WebkitLineClamp: "3",
129+
WebkitBoxOrient: "vertical",
130+
overflow: "hidden",
131+
textOverflow: "ellipsis",
132+
paddingBottom: "0.1em",
133+
}}
134+
>
135+
{title}
136+
</div>
137+
<div tw="flex items-center justify-between">
138+
<div tw="flex flex-col">
139+
<div
140+
tw="flex text-2xl text-neutral-100"
141+
style={{ paddingBottom: "0.1em" }}
142+
>
143+
{author}
144+
</div>
145+
<div
146+
tw="text-xl text-neutral-400"
147+
style={{ paddingBottom: "0.1em" }}
148+
>
149+
{`${formatDate(date)} · ${readTime} min read`}
150+
</div>
151+
</div>
152+
</div>
139153
</div>
140154
</div>
141155
</div>
142156
),
143157
{
144158
fonts: [
145159
{
146-
name: "Inter Latin",
147-
data: fontData,
160+
name: "Lato",
161+
data: regularFontData,
162+
style: "normal",
163+
weight: 400,
164+
},
165+
{
166+
name: "Lato-Bold",
167+
data: boldFontData,
148168
style: "normal",
169+
weight: 700,
149170
},
150171
],
151172
height,
152173
width,
153174
},
154175
);
155-
} catch {
176+
} catch (err) {
177+
Sentry.captureException(err);
156178
return new Response(`Failed to generate the image`, {
157179
status: 500,
158180
});
159181
}
160182
}
183+
184+
function formatDate(dateString: string): string {
185+
try {
186+
let date: Date;
187+
if (dateString.includes(" ")) {
188+
// Handle the specific format from the URL
189+
const [datePart, timePart] = dateString.split(" ");
190+
const [year, month, day] = datePart.split("-");
191+
const [time] = timePart.split("."); // Remove milliseconds
192+
const isoString = `${year}-${month}-${day}T${time}Z`;
193+
date = new Date(isoString);
194+
} else {
195+
date = new Date(dateString);
196+
}
197+
198+
if (isNaN(date.getTime())) {
199+
throw new Error("Invalid date");
200+
}
201+
return date.toLocaleString("en-US", {
202+
month: "long",
203+
day: "numeric",
204+
year: "numeric",
205+
});
206+
} catch (error) {
207+
return "";
208+
}
209+
}

assets/Lato-Bold.ttf

71.6 KB
Binary file not shown.

assets/Lato-Regular.ttf

73.4 KB
Binary file not shown.

cdk/lambdas/uploadResize/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ exports.handler = async (event) => {
8181
return resizedImage.webp({ quality: 80 }).toBuffer();
8282
}
8383
} catch (error) {
84-
console.error(`Error resizing image to ${size.maxWidth}x${size.maxHeight}:`, error);
84+
console.error(
85+
`Error resizing image to ${size.maxWidth}x${size.maxHeight}:`,
86+
error,
87+
);
8588
throw error;
8689
}
8790
};

0 commit comments

Comments
 (0)