Skip to content

Commit 637fd86

Browse files
committed
Merge branch 'feature-branch' of https://github.com/Nil2000/codu into feature-branch
2 parents 0bd09a9 + d8a32da commit 637fd86

File tree

17 files changed

+368
-98
lines changed

17 files changed

+368
-98
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/(app)/articles/_client.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ const ArticlesPage = () => {
158158
</section>
159159
</div>
160160
<section className="col-span-4 hidden lg:block">
161-
<div className="mb-8 mt-2 border border-neutral-300 bg-white text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50">
161+
<div className="mb-8 mt-2 rounded border border-neutral-300 bg-white text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50">
162162
<Link href="/articles/join-our-6-week-writing-challenge-quohtgqb">
163163
<Image
164-
className="w-full"
164+
className="w-full rounded-t"
165165
src={challenge}
166166
alt={`"Codú Writing Challenge" text on white background`}
167167
/>
@@ -191,7 +191,7 @@ const ArticlesPage = () => {
191191
key={title}
192192
// only reason this is toLowerCase is to make url look nicer. Not needed for functionality
193193
href={`/articles?tag=${title.toLowerCase()}`}
194-
className="border border-neutral-300 bg-white px-6 py-2 text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50"
194+
className="rounded border border-neutral-300 bg-white px-6 py-2 text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50"
195195
>
196196
{getCamelCaseFromLower(title)}
197197
</Link>

app/(app)/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ const Home = async () => {
5252
<TrendingPosts session={session} />
5353
</Suspense>
5454
<section className="col-span-5 hidden lg:block">
55-
<div className="mb-8 mt-2 border border-neutral-300 bg-white text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50">
55+
<div className="mb-8 mt-2 rounded border border-neutral-300 bg-white text-neutral-900 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-50">
5656
<Link href="/articles/join-our-6-week-writing-challenge-quohtgqb">
5757
<Image
58-
className="w-full"
58+
className="w-full rounded-t"
5959
src={challenge}
6060
alt={`"Codú Writing Challenge" text on white background`}
6161
/>

app/og/route.tsx

Lines changed: 168 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { ImageResponse } from "next/og";
2+
import * as Sentry from "@sentry/nextjs";
3+
import { Stars, Waves } from "@/components/background/background";
24

35
export const runtime = "edge";
46

@@ -8,75 +10,200 @@ const width = 1200;
810
export async function GET(request: Request) {
911
try {
1012
const { searchParams } = new URL(request.url);
13+
const origin = `${request.headers.get("x-forwarded-proto") || "http"}://${request.headers.get("host")}`;
1114

12-
// ?title=<title>
13-
const hasTitle = searchParams.has("title");
14-
const title = hasTitle
15-
? searchParams.get("title")?.slice(0, 100)
16-
: "My default title";
15+
const title = searchParams.get("title");
16+
const author = searchParams.get("author");
17+
const readTime = searchParams.get("readTime");
18+
const date = searchParams.get("date");
1719

18-
const fontData = await fetch(
19-
new URL(
20-
"https://og-playground.vercel.app/inter-latin-ext-700-normal.woff",
21-
import.meta.url,
22-
),
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),
2330
).then((res) => res.arrayBuffer());
2431

2532
return new ImageResponse(
2633
(
2734
<div
28-
tw="bg-black flex flex-col h-full w-full justify-center"
29-
style={{ padding: "0 88px" }}
35+
tw="flex flex-col h-full w-full"
36+
style={{
37+
fontFamily: "'Lato'",
38+
backgroundColor: "#1d1b36",
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%",
46+
}}
3047
>
31-
<img
32-
alt="Codu Logo"
48+
<Waves
3349
style={{
3450
position: "absolute",
35-
height: "28px",
36-
width: "86px",
37-
top: "88px",
38-
left: "86px",
51+
top: 0,
52+
left: 0,
53+
width,
54+
height,
3955
}}
40-
src="https://www.codu.co/_next/image?url=%2Fimages%2Fcodu.png&w=1920&q=75"
4156
/>
42-
<div tw="flex relative">
43-
<div
44-
style={{
45-
color: "white",
46-
fontSize: "64px",
47-
lineHeight: 1,
48-
fontWeight: "800",
49-
letterSpacing: "-.025em",
50-
lineClamp: 3,
51-
}}
52-
>
53-
{`${title}`}
57+
<Stars
58+
style={{
59+
position: "absolute",
60+
top: 0,
61+
left: 0,
62+
width,
63+
height,
64+
}}
65+
/>
66+
<div
67+
style={{
68+
position: "absolute",
69+
top: "50px",
70+
left: 0,
71+
right: 0,
72+
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
73+
}}
74+
/>
75+
<div
76+
style={{
77+
position: "absolute",
78+
bottom: "50px",
79+
left: 0,
80+
right: 0,
81+
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
82+
}}
83+
/>
84+
<div
85+
style={{
86+
position: "absolute",
87+
left: "50px",
88+
top: 0,
89+
bottom: 0,
90+
width: "40px",
91+
borderLeft: "1px solid rgba(255, 255, 255, 0.1)",
92+
}}
93+
/>
94+
<div
95+
style={{
96+
position: "absolute",
97+
right: "50px",
98+
top: 0,
99+
bottom: 0,
100+
width: "40px",
101+
borderRight: "1px solid rgba(255, 255, 255, 0.1)",
102+
}}
103+
/>
104+
<img
105+
alt="planet"
106+
tw="h-[528px] w-[528px] absolute right-[-170px] top-[-170px]"
107+
src={`${origin}/images/og/planet.png`}
108+
/>
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>
54153
</div>
55-
<div
56-
tw="h-4 w-72 absolute left-0 bg-gradient-to-r bg-red-500"
57-
style={{
58-
bottom: "-3rem",
59-
backgroundImage: "linear-gradient(to right, #fb923c, #db2777)",
60-
}}
61-
></div>
62154
</div>
63155
</div>
64156
),
65157
{
66158
fonts: [
67159
{
68-
name: "Inter Latin",
69-
data: fontData,
160+
name: "Lato",
161+
data: regularFontData,
70162
style: "normal",
163+
weight: 400,
164+
},
165+
{
166+
name: "Lato-Bold",
167+
data: boldFontData,
168+
style: "normal",
169+
weight: 700,
71170
},
72171
],
73172
height,
74173
width,
75174
},
76175
);
77-
} catch {
176+
} catch (err) {
177+
Sentry.captureException(err);
78178
return new Response(`Failed to generate the image`, {
79179
status: 500,
80180
});
81181
}
82182
}
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/avatarResize/index.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ exports.handler = async (event) => {
1111
const bucket = event.Records[0].s3.bucket.name;
1212
const key = event.Records[0].s3.object.key;
1313

14-
console.log({ bucket, key });
15-
1614
try {
1715
// Check if the image has already been resized
1816
const headParams = {
@@ -21,10 +19,10 @@ exports.handler = async (event) => {
2119
};
2220

2321
const headResponse = await s3.send(new HeadObjectCommand(headParams));
24-
const metadata = headResponse.Metadata || {};
22+
const s3Metadata = headResponse.Metadata || {};
2523
const contentType = headResponse.ContentType;
2624

27-
if (metadata.resized === "true") {
25+
if (s3Metadata.resized === "true") {
2826
return {
2927
statusCode: 200,
3028
body: JSON.stringify({ message: "Image already resized. Skipping." }),
@@ -39,14 +37,13 @@ exports.handler = async (event) => {
3937

4038
const response = await s3.send(new GetObjectCommand(getParams));
4139
const stream = response.Body;
42-
console.log({ response, stream });
40+
4341
if (!stream) throw new Error("BodyStream is empty");
4442

4543
const imageBuffer = Buffer.concat(await stream.toArray());
46-
const sharpImage = sharp(imageBuffer);
4744

4845
// Resize the image
49-
const resizedImageBuffer = await sharpImage
46+
const resizedImageBuffer = await sharp(imageBuffer)
5047
.resize({ width: 220, height: 220, fit: "cover" })
5148
.toBuffer();
5249

@@ -56,7 +53,7 @@ exports.handler = async (event) => {
5653
Bucket: bucket,
5754
Key: key,
5855
Body: resizedImageBuffer,
59-
Metadata: { ...metadata, resized: "true" },
56+
Metadata: { ...s3Metadata, resized: "true" },
6057
ContentType: contentType,
6158
}),
6259
);

0 commit comments

Comments
 (0)