Skip to content

Commit 8951d23

Browse files
committed
adding speakers share links
1 parent eaf1bc2 commit 8951d23

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

client/src/pages/conf/sections/SpeakersSection.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,30 @@ interface SpeakersSectionProps {
2828
confYear: string;
2929
}
3030

31+
// Pick a topic emoji from the session title. Falls back to 🎤.
32+
const getTopicEmoji = (title: string | undefined): string => {
33+
if (!title) return "🎤";
34+
const t = title.toLowerCase();
35+
if (/keynote|featured/.test(t)) return "🎤";
36+
if (/spacely|best practices/.test(t)) return "🚀";
37+
if (/behind the scenes|under the hood|architecture/.test(t)) return "🏗️";
38+
if (/distributed lock|saga|coordination/.test(t)) return "🔒";
39+
if (/change feed/.test(t)) return "🔄";
40+
if (/query|index/.test(t)) return "🔍";
41+
if (/fraud|event sourc/.test(t)) return "⚡";
42+
if (/microservice|event-driven/.test(t)) return "📡";
43+
if (/migrat/.test(t)) return "🔀";
44+
if (/import/.test(t)) return "📥";
45+
if (/mcp|identity|security|secur/.test(t)) return "🛡️";
46+
if (/rag|vector|hybrid search/.test(t)) return "🧭";
47+
if (/memory|agent|llm|\bai\b/.test(t)) return "🤖";
48+
if (/multi.?cloud|any cloud|one codebase/.test(t)) return "☁️";
49+
if (/cost|ru\b/.test(t)) return "💰";
50+
if (/data model|modeling/.test(t)) return "🗂️";
51+
if (/dev(elopment)? env|environment|copilot|tool/.test(t)) return "🛠️";
52+
return "🎤";
53+
};
54+
3155
const SpeakersSection = ({ confYear }: SpeakersSectionProps) => {
3256
const allSpeakers: Speaker[] = speakersData as unknown as Speaker[];
3357
const speakers = allSpeakers.filter((s) => s.confirmed !== false);
@@ -229,6 +253,95 @@ const SpeakersSection = ({ confYear }: SpeakersSectionProps) => {
229253
<p className={styles.speakerModalBioText}>{selected.bio}</p>
230254
</div>
231255
)}
256+
257+
{(() => {
258+
const shareHash = `#speaker/${selected.slug}`;
259+
const shareUrl =
260+
typeof window !== "undefined"
261+
? `${window.location.origin}${window.location.pathname}${shareHash}`
262+
: shareHash;
263+
const sessionTitle = selected.session?.title;
264+
const roleCompany = [selected.role, selected.company].filter(Boolean).join(", ");
265+
const topicEmoji = getTopicEmoji(sessionTitle);
266+
// Short-form for X (keep under ~240 chars before URL)
267+
const twitterText = sessionTitle
268+
? `${topicEmoji} Catch ${selected.name}${roleCompany ? ` (${roleCompany})` : ""} at #AzureCosmosDBConf ${confYear}: "${sessionTitle}"`
269+
: `${topicEmoji} Catch ${selected.name}${roleCompany ? ` (${roleCompany})` : ""} at #AzureCosmosDBConf ${confYear}.`;
270+
// Longer-form for LinkedIn
271+
const linkedInText = sessionTitle
272+
? `${topicEmoji} I'm looking forward to ${selected.name}${roleCompany ? ` (${roleCompany})` : ""} at Azure Cosmos DB Conf ${confYear} — session: "${sessionTitle}". Free, virtual, April 28. #AzureCosmosDBConf #AzureCosmosDB`
273+
: `${topicEmoji} I'm looking forward to ${selected.name}${roleCompany ? ` (${roleCompany})` : ""} at Azure Cosmos DB Conf ${confYear}. Free, virtual, April 28. #AzureCosmosDBConf #AzureCosmosDB`;
274+
const emailSubject = sessionTitle
275+
? `${selected.name} at Azure Cosmos DB Conf ${confYear}${sessionTitle}`
276+
: `${selected.name} at Azure Cosmos DB Conf ${confYear}`;
277+
const emailBody = `${linkedInText}\n\n${shareUrl}`;
278+
const encodedUrl = encodeURIComponent(shareUrl);
279+
const twitterUrl = `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodeURIComponent(twitterText)}`;
280+
const linkedInUrl = `https://www.linkedin.com/feed/?shareActive=true&text=${encodeURIComponent(
281+
`${linkedInText}\n\n${shareUrl}`
282+
)}`;
283+
const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`;
284+
const mailUrl = `mailto:?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`;
285+
const copyLink = (e: React.MouseEvent<HTMLButtonElement>) => {
286+
e.preventDefault();
287+
if (typeof window === "undefined" || !navigator.clipboard) return;
288+
navigator.clipboard.writeText(shareUrl).catch(() => {});
289+
};
290+
return (
291+
<div className={styles.newsCardShareRow} aria-label="Share this speaker">
292+
<span className={styles.newsCardShareLabel}>Share:</span>
293+
<a
294+
className={styles.newsCardShareButton}
295+
href={twitterUrl}
296+
target="_blank"
297+
rel="noopener noreferrer"
298+
title="Share on X (Twitter)"
299+
aria-label="Share on X"
300+
>
301+
𝕏
302+
</a>
303+
<a
304+
className={styles.newsCardShareButton}
305+
href={linkedInUrl}
306+
target="_blank"
307+
rel="noopener noreferrer"
308+
title="Share on LinkedIn"
309+
aria-label="Share on LinkedIn"
310+
>
311+
in
312+
</a>
313+
<a
314+
className={styles.newsCardShareButton}
315+
href={facebookUrl}
316+
target="_blank"
317+
rel="noopener noreferrer"
318+
title="Share on Facebook"
319+
aria-label="Share on Facebook"
320+
>
321+
f
322+
</a>
323+
<a
324+
className={styles.newsCardShareButton}
325+
href={mailUrl}
326+
target="_blank"
327+
rel="noopener noreferrer"
328+
title="Share by email"
329+
aria-label="Share by email"
330+
>
331+
332+
</a>
333+
<button
334+
type="button"
335+
className={styles.newsCardShareButton}
336+
onClick={copyLink}
337+
title="Copy link to this speaker"
338+
aria-label="Copy link"
339+
>
340+
🔗
341+
</button>
342+
</div>
343+
);
344+
})()}
232345
</div>
233346
</div>
234347
)}

0 commit comments

Comments
 (0)