Skip to content

Commit 7b2d6db

Browse files
authored
Merge pull request #111 from techdiary-dev/copilot/update-profile-page-og-image
Add dynamic OG image for profile pages
2 parents 6b0695f + c46531b commit 7b2d6db

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { getUserByUsername } from "@/backend/services/user.action";
2+
import getFileUrl from "@/utils/getFileUrl";
3+
import { sanitizedUsername } from "@/lib/utils";
4+
import { ImageResponse } from "next/og";
5+
import { readFile } from "node:fs/promises";
6+
import { join } from "node:path";
7+
8+
interface ProfilePageProps {
9+
params: Promise<{
10+
username: string;
11+
}>;
12+
}
13+
14+
export const size = {
15+
width: 1200,
16+
height: 630,
17+
};
18+
export const contentType = "image/png";
19+
20+
const getFileLocation = async (relativePath: string) => {
21+
const fileData = await readFile(join(process.cwd(), "public", relativePath));
22+
return Uint8Array.from(fileData).buffer;
23+
};
24+
25+
export default async function Image(options: ProfilePageProps) {
26+
const { username } = await options.params;
27+
const sanitized = sanitizedUsername(username);
28+
const profile = await getUserByUsername(sanitized, [
29+
"name",
30+
"username",
31+
"bio",
32+
"profile_photo",
33+
"designation",
34+
]);
35+
36+
const displayName = profile?.name ?? sanitized;
37+
const displayUsername = profile?.username ?? sanitized;
38+
const bio = profile?.bio ?? null;
39+
const designation = profile?.designation ?? null;
40+
const profilePhotoUrl = profile?.profile_photo
41+
? getFileUrl(profile.profile_photo)
42+
: null;
43+
44+
return new ImageResponse(
45+
(
46+
<div
47+
style={{
48+
background: "white",
49+
fontFamily: "BANGLA_FONT",
50+
fontWeight: 400,
51+
display: "flex",
52+
flexDirection: "column",
53+
justifyContent: "space-between",
54+
height: "100%",
55+
width: "100%",
56+
}}
57+
>
58+
{/* Main content area */}
59+
<div
60+
style={{
61+
display: "flex",
62+
flex: 1,
63+
background: "rgb(54%, 61%, 100%)",
64+
alignItems: "center",
65+
justifyContent: "center",
66+
padding: 40,
67+
gap: 40,
68+
}}
69+
>
70+
{/* Profile photo */}
71+
{profilePhotoUrl ? (
72+
<img
73+
src={profilePhotoUrl}
74+
alt={displayName}
75+
style={{
76+
width: 160,
77+
height: 160,
78+
borderRadius: "50%",
79+
objectFit: "cover",
80+
border: "4px solid white",
81+
flexShrink: 0,
82+
}}
83+
/>
84+
) : null}
85+
86+
{/* Text info */}
87+
<div
88+
style={{
89+
display: "flex",
90+
flexDirection: "column",
91+
gap: 12,
92+
color: "white",
93+
}}
94+
>
95+
<h1
96+
style={{
97+
fontSize: 52,
98+
margin: 0,
99+
fontFamily: "BANGLA_FONT",
100+
lineHeight: 1.2,
101+
}}
102+
>
103+
{displayName}
104+
</h1>
105+
<p
106+
style={{
107+
fontSize: 28,
108+
margin: 0,
109+
opacity: 0.85,
110+
}}
111+
>
112+
@{displayUsername}
113+
</p>
114+
{designation ? (
115+
<p
116+
style={{
117+
fontSize: 24,
118+
margin: 0,
119+
opacity: 0.8,
120+
}}
121+
>
122+
{designation}
123+
</p>
124+
) : null}
125+
{bio ? (
126+
<p
127+
style={{
128+
fontSize: 22,
129+
margin: 0,
130+
opacity: 0.75,
131+
maxWidth: 600,
132+
lineHeight: 1.4,
133+
}}
134+
>
135+
{bio.length > 120 ? bio.slice(0, 117) + "..." : bio}
136+
</p>
137+
) : null}
138+
</div>
139+
</div>
140+
141+
{/* Footer */}
142+
<div
143+
style={{
144+
display: "flex",
145+
justifyContent: "flex-end",
146+
padding: 30,
147+
}}
148+
>
149+
{/* Logo */}
150+
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
151+
<img
152+
style={{ height: 48 }}
153+
src={(await getFileLocation("logo-lg.png")) as any}
154+
alt="logo"
155+
/>
156+
<p style={{ fontSize: 28 }}>TechDiary</p>
157+
</div>
158+
</div>
159+
</div>
160+
),
161+
{
162+
...size,
163+
fonts: [
164+
{
165+
name: "BANGLA_FONT",
166+
data: await getFileLocation("fonts/HindSiliguri-Regular.ttf"),
167+
style: "normal",
168+
weight: 400,
169+
},
170+
],
171+
}
172+
);
173+
}

0 commit comments

Comments
 (0)