Skip to content

Commit 32f4571

Browse files
Feat/og talk (#214)
Co-authored-by: Yoann Fleury <yoann.fleury@yahoo.com>
1 parent 4f38a28 commit 32f4571

File tree

13 files changed

+625
-24
lines changed

13 files changed

+625
-24
lines changed

src/content/people/vu-tuan-anh/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: Tuan Anh Vu
33
job: Project Manager, PM & Agile Trainer
4-
avatar: ./vu-tuan-anh.webp
4+
avatar: ./vu-tuan-anh.jpeg
55
company:
66
title: NashTech
77
href: https://www.nashtechglobal.com/
90.3 KB
Loading
-39 KB
Binary file not shown.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
title: "Navigating and Redesigning Payment Systems for Global Connection"
2+
title: "Navigating and Redesigning Payment Systems for Global Connection"
33
speakers:
44
- id: adaku-nwakanma
55
language: english
66
---
77

8-
​​Global payment systems are intended to connect us all, yet they often overlook the unique challenges faced by people in underserved economies. This talk dives into the creative ways Nigerians navigate and design payment solutions when major platforms fail to address local realities. For freelancers and entrepreneurs from Nigeria and other similar socioeconomic contexts, accessing international clients or services isn’t always straightforward. Traditional payment systems can be restrictive, making it difficult to send or receive funds, and global platforms often lack support for local currencies, banking structures, and reliable access.
8+
Global payment systems are intended to connect us all, yet they often overlook the unique challenges faced by people in underserved economies. This talk dives into the creative ways Nigerians navigate and design payment solutions when major platforms fail to address local realities. For freelancers and entrepreneurs from Nigeria and other similar socioeconomic contexts, accessing international clients or services isn’t always straightforward. Traditional payment systems can be restrictive, making it difficult to send or receive funds, and global platforms often lack support for local currencies, banking structures, and reliable access.
99

10-
I will share insights from my personal experience as a freelancer from Nigeria, covering both the challenges and the ingenious, often community-driven solutions Nigerians have developed to thrive financially. From mobile banking innovations and peer-to-peer transactions to informal digital networks, these strategies reflect a deep cultural and technological adaptability.
10+
I will share insights from my personal experience as a freelancer from Nigeria, covering both the challenges and the ingenious, often community-driven solutions Nigerians have developed to thrive financially. From mobile banking innovations and peer-to-peer transactions to informal digital networks, these strategies reflect a deep cultural and technological adaptability.
1111

12-
By examining how people work around these gaps, this talk will offer practical takeaways for designers and developers aiming to create more flexible and inclusive financial tools. Attendees will gain a deeper understanding of the resilience in these communities and learn how to incorporate similar adaptability, inclusivity, and human-centred thinking into global design solutions for payment systems.
12+
By examining how people work around these gaps, this talk will offer practical takeaways for designers and developers aiming to create more flexible and inclusive financial tools. Attendees will gain a deeper understanding of the resilience in these communities and learn how to incorporate similar adaptability, inclusivity, and human-centred thinking into global design solutions for payment systems.

src/dynamic-images/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ async function getAstroImageBuffer(image: ImageMetadata) {
183183

184184
export async function getAstroImageBase64(image: ImageMetadata) {
185185
const { buffer, fileType } = await getAstroImageBuffer(image);
186-
return `data:image/${fileType};charset=utf-8;base64, ${buffer.toString("base64")}`;
186+
return `data:image/${fileType};base64, ${buffer.toString("base64")}`;
187187
}
188188

189189
type GetStaticPathItemWithGeneric<Props> = {

src/lib/events.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ export function isEventPublished(
1515
return status !== "draft";
1616
}
1717

18-
export function getEventsCollection() {
19-
return getCollection("events", ({ data }) =>
20-
import.meta.env.PROD ? isEventPublished(data.status) : true,
21-
);
18+
export async function getEventsCollection() {
19+
return (
20+
await getCollection("events", ({ data }) =>
21+
import.meta.env.PROD ? isEventPublished(data.status) : true,
22+
)
23+
).sort((a, b) => dayjs(b.data.date).diff(a.data.date));
2224
}
2325

2426
export function getEventSubPagesCollection(
@@ -222,8 +224,6 @@ export async function getPersonEvents(
222224
) {
223225
return (
224226
(await getEventsCollection())
225-
// Order with the most recent first
226-
.sort((a, b) => dayjs(b.data.date).diff(a.data.date))
227227
// We don't want cancelled events and just the one the person is in
228228
.filter(
229229
(event) =>

src/pages/events/[id]/dynamic-images/_tickets-available.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,15 @@ export const ticketsAvailable = (config: {
8686
display: "flex",
8787
justifyContent: "flex-start",
8888
marginBottom: 24,
89+
marginTop: -12,
8990
}}
9091
>
9192
<div
9293
style={{
9394
display: "flex",
94-
background: COLORS.primary,
95-
color: COLORS.black,
96-
padding: "32px 48px",
97-
fontSize: 48 * config.fontScaling,
95+
color: COLORS.primary,
96+
fontSize: 64 * config.fontScaling,
9897
fontWeight: 500,
99-
borderRadius: 12,
10098
textTransform: "uppercase",
10199
}}
102100
>

src/pages/events/[id]/dynamic-images/_utils.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ export const getEventStaticPaths = async () => {
44
const events = await getCollection("events");
55

66
return Promise.all(
7-
events.map(async (event) => ({
8-
params: { id: event.id },
9-
props: {
10-
event,
11-
coOrganizers: await getEntries(event.data.coOrganizers ?? []),
12-
},
13-
})),
7+
events.map(async (event) => {
8+
const talksForEvent = await getCollection("talks", (item) =>
9+
event.data.schedule
10+
?.map((activity) => activity.slug?.id)
11+
.includes(item.id),
12+
);
13+
14+
return {
15+
params: { id: event.id },
16+
17+
props: {
18+
event,
19+
talks: talksForEvent,
20+
coOrganizers: await getEntries(event.data.coOrganizers ?? []),
21+
},
22+
};
23+
}),
1424
);
1525
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { apiImageGenerator } from "@/dynamic-images";
2+
3+
const methods = await apiImageGenerator({
4+
modules: import.meta.glob("./_*.tsx", { eager: true }),
5+
});
6+
export const getStaticPaths = methods.getStaticPaths;
7+
export const GET = methods.GET;
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { getEventDisplayDate } from "@/lib/events";
2+
import peoplePlaceholder from "@/assets/images/people-placeholder.jpeg";
3+
import {
4+
COLORS,
5+
generateImageMethods,
6+
getAstroImageBase64,
7+
} from "@/dynamic-images/utils";
8+
import { getEventTalkStaticPaths } from "./_utils";
9+
import { Frame } from "@/dynamic-images/components/Frame";
10+
import { BgImage } from "@/dynamic-images/components/BgImage";
11+
import { LogoWithFriends } from "@/dynamic-images/components/LogoWithFriends";
12+
13+
export default generateImageMethods({
14+
width: 1920,
15+
height: 1080,
16+
getStaticPaths: getEventTalkStaticPaths,
17+
render: async (props) => {
18+
const postCover = await getAstroImageBase64(props.event.data.image.src);
19+
const coOrganizersLogos = await Promise.all(
20+
props.coOrganizers.map(
21+
async (coOrganiser) =>
22+
await getAstroImageBase64(coOrganiser.data.logos.noBgSquare),
23+
),
24+
);
25+
const speakersImages = await Promise.all(
26+
props.speakers.map(
27+
async (speaker) =>
28+
await getAstroImageBase64(speaker.data.avatar ?? peoplePlaceholder),
29+
),
30+
);
31+
return (
32+
<Frame {...props.dynamicImage} style={{ padding: 96 }}>
33+
<BgImage
34+
src={postCover}
35+
width={props.dynamicImage.width}
36+
height={props.dynamicImage.height}
37+
/>
38+
39+
<div
40+
style={{
41+
zIndex: 100,
42+
flex: 1,
43+
display: "flex",
44+
flexDirection: "column",
45+
width: "100%",
46+
justifyContent: "space-between",
47+
}}
48+
>
49+
<LogoWithFriends logos={coOrganizersLogos} />
50+
51+
<div
52+
style={{
53+
display: "flex",
54+
alignItems: "center",
55+
gap: 80,
56+
paddingBottom: 24,
57+
}}
58+
>
59+
<div
60+
style={{
61+
display: "flex",
62+
flex: 1,
63+
flexDirection: "column",
64+
gap: 20,
65+
}}
66+
>
67+
<div
68+
style={{
69+
display: "flex",
70+
fontSize: 80,
71+
fontWeight: 500,
72+
lineHeight: 1,
73+
marginTop: -8,
74+
color: COLORS.primary,
75+
textWrap: "balance",
76+
}}
77+
>
78+
{props.talk.data.title}
79+
</div>
80+
81+
<div
82+
style={{
83+
display: "flex",
84+
fontSize: 40,
85+
fontWeight: 500,
86+
letterSpacing: 4,
87+
marginBottom: 48,
88+
}}
89+
>
90+
By{" "}
91+
{props.talk.speakers
92+
.map((speaker) => speaker.data.name)
93+
.join(", ")}
94+
</div>
95+
96+
<div
97+
style={{
98+
display: "flex",
99+
flexWrap: "wrap",
100+
alignItems: "center",
101+
columnGap: 48,
102+
rowGap: 24,
103+
}}
104+
>
105+
<div
106+
style={{
107+
display: "flex",
108+
alignItems: "center",
109+
gap: 12,
110+
fontSize: 48,
111+
fontWeight: 500,
112+
lineHeight: 1,
113+
}}
114+
>
115+
<svg
116+
viewBox="0 0 24 24"
117+
style={{
118+
flex: "none",
119+
opacity: 0.6,
120+
width: "1em",
121+
height: "1em",
122+
}}
123+
>
124+
<path
125+
fill="currentColor"
126+
d="M19 19H5V8h14m-3-7v2H8V1H6v2H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2h-1V1m-1 11h-5v5h5z"
127+
/>
128+
</svg>
129+
{getEventDisplayDate(props.event)}
130+
</div>
131+
132+
{!!props.event.data.location?.name && (
133+
<div
134+
style={{
135+
display: "flex",
136+
gap: 12,
137+
alignItems: "center",
138+
fontSize: 48,
139+
fontWeight: 500,
140+
lineHeight: 1.2,
141+
textWrap: "balance",
142+
}}
143+
>
144+
<svg
145+
viewBox="0 0 24 24"
146+
style={{
147+
flex: "none",
148+
opacity: 0.6,
149+
width: "1em",
150+
height: "1em",
151+
}}
152+
>
153+
<path
154+
fill="currentColor"
155+
d="M12 11.5A2.5 2.5 0 0 1 9.5 9A2.5 2.5 0 0 1 12 6.5A2.5 2.5 0 0 1 14.5 9a2.5 2.5 0 0 1-2.5 2.5M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7"
156+
/>
157+
</svg>
158+
{props.event.data.location.name}
159+
</div>
160+
)}
161+
</div>
162+
</div>
163+
<div
164+
style={{
165+
position: "relative",
166+
display: "flex",
167+
flex: "none",
168+
gap: 20,
169+
width: 512,
170+
height: 512,
171+
top: speakersImages.length > 1 ? -40 : 0,
172+
left: speakersImages.length > 1 ? -40 : 0,
173+
transform: `scale(${0.1 * speakersImages.length + 1}`,
174+
}}
175+
>
176+
{speakersImages.map((imgSrc, index) => {
177+
const size =
178+
speakersImages.length > 1
179+
? 1024 / (speakersImages.length * 1.5)
180+
: 512;
181+
return (
182+
<img
183+
src={imgSrc}
184+
style={{
185+
position: "absolute",
186+
top: (400 / speakersImages.length) * index,
187+
left: (400 / speakersImages.length) * index,
188+
width: size,
189+
height: size,
190+
borderRadius: 20,
191+
boxShadow: "0 10px 20px rgba(0,0,0,0.4)",
192+
}}
193+
/>
194+
);
195+
})}
196+
</div>
197+
</div>
198+
199+
<div
200+
style={{
201+
display: "flex",
202+
justifyContent: "space-between",
203+
alignItems: "flex-end",
204+
}}
205+
>
206+
<div
207+
style={{
208+
display: "flex",
209+
fontSize: 32,
210+
fontWeight: 500,
211+
lineHeight: 1.2,
212+
textTransform: "uppercase",
213+
opacity: 0.6,
214+
}}
215+
>
216+
{props.event.data.city}, {props.event.data.country}
217+
</div>
218+
<div
219+
style={{
220+
display: "flex",
221+
fontSize: 32,
222+
fontWeight: 500,
223+
lineHeight: 1.2,
224+
textTransform: "uppercase",
225+
opacity: 0.6,
226+
}}
227+
>
228+
www.forkit.community
229+
</div>
230+
</div>
231+
</div>
232+
</Frame>
233+
);
234+
},
235+
});

0 commit comments

Comments
 (0)