Skip to content

Commit 1d8c807

Browse files
committed
Restore Mastodon/LinkedIn links in author box
The article author box lost the Mastodon and LinkedIn (and Launchpad / website) profile links that were added during the GSoC contributor selection process (candidate repo issue #54 / PR #60); the unmerged work was dropped when the Next.js rebuild branched from an earlier point. - data/authors.ts: add an optional `links[]` array (AuthorLink) and populate the verified profiles for Till, Mike, Dheeraj and Carlos. `role` is kept as-is (no rename); no profile URLs are invented for authors who have none. - AuthorCard: render a unified, ordered link list (Email, GitHub, then the extra profile links with per-kind icons) in both the desktop card and the mobile "Follow" dropdown. Authors without social profiles are unaffected. - OpenPrintingCard (/news): add Mastodon and LinkedIn links and correct the label to "The Linux Foundation" for consistency with the rest of the site. - components/icons.tsx: new shared MastodonIcon (lucide ships none); the footer now imports it instead of keeping a duplicate copy.
1 parent 6d11bb0 commit 1d8c807

5 files changed

Lines changed: 170 additions & 49 deletions

File tree

components/AuthorCard.tsx

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
"use client";
22

33
import { useState, useEffect, useRef } from "react";
4+
import type { ReactNode } from "react";
45
import Image from "next/image";
5-
import { MapPin, Mail, Github } from "lucide-react";
6+
import { MapPin, Mail, Github, Linkedin, Globe, Link2 } from "lucide-react";
67
import { cn } from "@/lib/utils";
78
import authors from "@/data/authors";
9+
import type { AuthorLink } from "@/data/authors";
810
import { getAuthorImageSrc } from "@/lib/utils";
11+
import { MastodonIcon } from "@/components/icons";
12+
13+
function linkIcon(kind: AuthorLink["kind"], size: number): ReactNode {
14+
switch (kind) {
15+
case "linkedin":
16+
return <Linkedin size={size} className="text-muted-foreground" />;
17+
case "website":
18+
return <Globe size={size} className="text-muted-foreground" />;
19+
case "mastodon":
20+
return (
21+
<MastodonIcon
22+
className="text-muted-foreground"
23+
style={{ width: size, height: size }}
24+
/>
25+
);
26+
default:
27+
return <Link2 size={size} className="text-muted-foreground" />;
28+
}
29+
}
930

1031
interface Props {
1132
authorKey: string;
@@ -43,6 +64,48 @@ export default function AuthorCard({ authorKey, className }: Props) {
4364

4465
const imgSrc = getAuthorImageSrc(author.image);
4566

67+
const extraLinks = (author.links ?? []).filter(
68+
(l) => typeof l?.href === "string" && l.href.trim() !== ""
69+
);
70+
71+
const linkItems: {
72+
label: string;
73+
href: string;
74+
iconSm: ReactNode;
75+
iconMd: ReactNode;
76+
external: boolean;
77+
}[] = [
78+
...(author.email
79+
? [
80+
{
81+
label: "Email",
82+
href: author.email,
83+
iconSm: <Mail size={14} className="text-muted-foreground" />,
84+
iconMd: <Mail size={16} />,
85+
external: false,
86+
},
87+
]
88+
: []),
89+
...(author.github
90+
? [
91+
{
92+
label: "GitHub",
93+
href: author.github,
94+
iconSm: <Github size={14} className="text-muted-foreground" />,
95+
iconMd: <Github size={16} />,
96+
external: true,
97+
},
98+
]
99+
: []),
100+
...extraLinks.map((l) => ({
101+
label: l.label,
102+
href: l.href,
103+
iconSm: linkIcon(l.kind, 14),
104+
iconMd: linkIcon(l.kind, 16),
105+
external: true,
106+
})),
107+
];
108+
46109
return (
47110
<>
48111
<div className="lg:hidden flex items-center gap-3 px-4 py-4">
@@ -83,27 +146,18 @@ export default function AuthorCard({ authorKey, className }: Props) {
83146
</div>
84147
)}
85148

86-
{author.email && (
87-
<a
88-
href={author.email}
89-
className="flex items-center gap-2 px-3 py-2 text-muted-foreground hover:text-foreground hover:bg-accent"
90-
>
91-
<Mail size={14} className="text-muted-foreground" />
92-
<span className="text-sm">Email</span>
93-
</a>
94-
)}
95-
96-
{author.github && (
149+
{linkItems.map((item) => (
97150
<a
98-
href={author.github}
99-
target="_blank"
100-
rel="noopener noreferrer"
151+
key={`${item.label}-${item.href}`}
152+
href={item.href}
153+
target={item.external ? "_blank" : undefined}
154+
rel={item.external ? "noopener noreferrer" : undefined}
101155
className="flex items-center gap-2 px-3 py-2 text-muted-foreground hover:text-foreground hover:bg-accent"
102156
>
103-
<Github size={14} className="text-muted-foreground" />
104-
<span className="text-sm">GitHub</span>
157+
{item.iconSm}
158+
<span className="text-sm">{item.label}</span>
105159
</a>
106-
)}
160+
))}
107161
</div>
108162
)}
109163
</div>
@@ -151,27 +205,18 @@ export default function AuthorCard({ authorKey, className }: Props) {
151205
)}
152206

153207
<div className="flex flex-col items-start pl-2 gap-3">
154-
{author.email && (
155-
<a
156-
href={author.email}
157-
className="inline-flex items-center gap-3 text-muted-foreground hover:text-foreground transition-colors duration-200"
158-
>
159-
<Mail size={16} />
160-
<span className="text-sm">Email</span>
161-
</a>
162-
)}
163-
164-
{author.github && (
208+
{linkItems.map((item) => (
165209
<a
166-
href={author.github}
167-
target="_blank"
168-
rel="noopener noreferrer"
210+
key={`${item.label}-${item.href}-desktop`}
211+
href={item.href}
212+
target={item.external ? "_blank" : undefined}
213+
rel={item.external ? "noopener noreferrer" : undefined}
169214
className="inline-flex items-center gap-3 text-muted-foreground hover:text-foreground transition-colors duration-200"
170215
>
171-
<Github size={16} />
172-
<span className="text-sm">GitHub</span>
216+
{item.iconMd}
217+
<span className="text-sm">{item.label}</span>
173218
</a>
174-
)}
219+
))}
175220
</div>
176221
</div>
177222
</div>

components/OpenPrintingCard.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"use client";
22

33
import { useState, useEffect, useRef } from "react";
4-
import { MapPin, Github, Globe } from "lucide-react";
4+
import { MapPin, Github, Globe, Linkedin } from "lucide-react";
55
import Image from "next/image";
66
import { cn, getAssetPath } from "@/lib/utils";
77
import { siteConfig } from "@/config/site.config";
8+
import { MastodonIcon } from "@/components/icons";
89

910
interface Props {
1011
className?: string;
@@ -69,7 +70,7 @@ export default function OpenPrintingCard({ className }: Props) {
6970
<div className="absolute right-0 top-full mt-2 w-48 bg-card rounded shadow-xl border border-border py-1 z-50">
7071
<div className="flex items-center gap-2 px-3 py-2 text-foreground">
7172
<MapPin size={16} className="text-muted-foreground" />
72-
<span className="text-sm">{siteConfig.brand.organization}</span>
73+
<span className="text-sm">The {siteConfig.brand.organization}</span>
7374
</div>
7475

7576
<a
@@ -89,6 +90,26 @@ export default function OpenPrintingCard({ className }: Props) {
8990
<Github size={16} className="text-muted-foreground" />
9091
<span className="text-sm">GitHub</span>
9192
</a>
93+
94+
<a
95+
href={siteConfig.destinations.mastodon}
96+
target="_blank"
97+
rel="noopener noreferrer"
98+
className="flex items-center gap-2 px-3 py-2 text-foreground hover:bg-muted"
99+
>
100+
<MastodonIcon className="w-4 h-4 text-muted-foreground" />
101+
<span className="text-sm">Mastodon</span>
102+
</a>
103+
104+
<a
105+
href={siteConfig.destinations.linkedin}
106+
target="_blank"
107+
rel="noopener noreferrer"
108+
className="flex items-center gap-2 px-3 py-2 text-foreground hover:bg-muted"
109+
>
110+
<Linkedin size={16} className="text-muted-foreground" />
111+
<span className="text-sm">LinkedIn</span>
112+
</a>
92113
</div>
93114
)}
94115
</div>
@@ -121,7 +142,7 @@ export default function OpenPrintingCard({ className }: Props) {
121142

122143
<div className="flex items-center gap-3 text-muted-foreground mb-4 pl-2 self-start">
123144
<MapPin size={16} />
124-
<span className="text-sm">{siteConfig.brand.organization}</span>
145+
<span className="text-sm">The {siteConfig.brand.organization}</span>
125146
</div>
126147

127148
<div className="flex flex-col items-start pl-2 gap-3 self-start">
@@ -142,6 +163,26 @@ export default function OpenPrintingCard({ className }: Props) {
142163
<Github size={18} />
143164
<span className="text-sm">GitHub</span>
144165
</a>
166+
167+
<a
168+
href={siteConfig.destinations.mastodon}
169+
target="_blank"
170+
rel="noopener noreferrer"
171+
className="inline-flex items-center gap-3 text-muted-foreground hover:text-primary"
172+
>
173+
<MastodonIcon className="w-[18px] h-[18px]" />
174+
<span className="text-sm">Mastodon</span>
175+
</a>
176+
177+
<a
178+
href={siteConfig.destinations.linkedin}
179+
target="_blank"
180+
rel="noopener noreferrer"
181+
className="inline-flex items-center gap-3 text-muted-foreground hover:text-primary"
182+
>
183+
<Linkedin size={18} />
184+
<span className="text-sm">LinkedIn</span>
185+
</a>
145186
</div>
146187
</div>
147188
</div>

components/footer.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@ import { siteConfig } from "@/config/site.config"
44
import { getAssetPath } from "@/lib/utils"
55
import Link from "next/link"
66
import { Github, Linkedin, Rss } from "lucide-react"
7-
8-
function MastodonIcon({ className }: { className?: string }) {
9-
return (
10-
<svg className={className} viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
11-
<path d="M21.327 8.566c0-4.339-2.843-5.61-2.843-5.61-1.433-.658-3.894-.935-6.451-.956h-.063c-2.557.021-5.016.298-6.45.956 0 0-2.843 1.272-2.843 5.61 0 .993-.019 2.181.012 3.441.103 4.243.778 8.425 4.701 9.463 1.809.479 3.362.579 4.612.51 2.268-.126 3.536-.766 3.536-.766l-.072-1.574s-1.619.511-3.338.449c-1.693-.063-3.476-.194-3.752-2.448a4.198 4.198 0 0 1-.037-.575s1.661.406 3.764.502c1.287.059 2.495-.075 3.724-.221 2.354-.28 4.405-1.724 4.663-3.043.405-2.089.372-5.098.372-5.098l-.001-.002zm-3.809 5.447h-2.494V8.818c0-1.143-.48-1.723-1.443-1.723-1.063 0-1.596.688-1.596 2.047v2.964h-2.48V9.142c0-1.359-.534-2.047-1.596-2.047-.962 0-1.443.58-1.443 1.723v5.195H4.972V8.648c0-1.142.291-2.048.875-2.717.601-.67 1.389-1.013 2.368-1.013 1.132 0 1.989.435 2.556 1.305l.551.924.551-.924c.568-.87 1.425-1.305 2.556-1.305.979 0 1.767.344 2.368 1.013.583.669.875 1.575.875 2.717v5.365z"/>
12-
</svg>
13-
)
14-
}
7+
import { MastodonIcon } from "@/components/icons"
158

169
const socialLinks = [
1710
{ icon: Github, href: siteConfig.destinations.github, label: "GitHub" },
@@ -25,7 +18,6 @@ export default function Footer() {
2518
<footer className="bg-neutral-100 dark:bg-black border-t border-border">
2619
<div className="max-w-6xl mx-auto px-6 py-16">
2720
<div className="grid grid-cols-1 md:grid-cols-5 gap-12 md:gap-8">
28-
{/* Brand */}
2921
<div className="md:col-span-2">
3022
<h3 className="text-lg font-semibold text-foreground mb-3 tracking-tight">{siteConfig.brand.name}</h3>
3123
<p className="text-sm text-muted-foreground leading-relaxed max-w-xs mb-6">
@@ -47,7 +39,6 @@ export default function Footer() {
4739
</div>
4840
</div>
4941

50-
{/* Link Columns */}
5142
{siteConfig.navigation.footer.map((section) => (
5243
<div key={section.title}>
5344
<h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-4">
@@ -82,7 +73,6 @@ export default function Footer() {
8273
</div>
8374
</div>
8475

85-
{/* Bottom bar */}
8676
<div className="border-t border-border">
8777
<div className="max-w-6xl mx-auto px-6 py-5 flex flex-col sm:flex-row items-center justify-between gap-3">
8878
<p className="text-xs text-muted-foreground">

components/icons.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { SVGProps } from "react"
2+
3+
export function MastodonIcon({ className, ...props }: SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg
6+
className={className}
7+
viewBox="0 0 24 24"
8+
fill="currentColor"
9+
xmlns="http://www.w3.org/2000/svg"
10+
aria-hidden="true"
11+
{...props}
12+
>
13+
<path d="M21.327 8.566c0-4.339-2.843-5.61-2.843-5.61-1.433-.658-3.894-.935-6.451-.956h-.063c-2.557.021-5.016.298-6.45.956 0 0-2.843 1.272-2.843 5.61 0 .993-.019 2.181.012 3.441.103 4.243.778 8.425 4.701 9.463 1.809.479 3.362.579 4.612.51 2.268-.126 3.536-.766 3.536-.766l-.072-1.574s-1.619.511-3.338.449c-1.693-.063-3.476-.194-3.752-2.448a4.198 4.198 0 0 1-.037-.575s1.661.406 3.764.502c1.287.059 2.495-.075 3.724-.221 2.354-.28 4.405-1.724 4.663-3.043.405-2.089.372-5.098.372-5.098l-.001-.002zm-3.809 5.447h-2.494V8.818c0-1.143-.48-1.723-1.443-1.723-1.063 0-1.596.688-1.596 2.047v2.964h-2.48V9.142c0-1.359-.534-2.047-1.596-2.047-.962 0-1.443.58-1.443 1.723v5.195H4.972V8.648c0-1.142.291-2.048.875-2.717.601-.67 1.389-1.013 2.368-1.013 1.132 0 1.989.435 2.556 1.305l.551.924.551-.924c.568-.87 1.425-1.305 2.556-1.305.979 0 1.767.344 2.368 1.013.583.669.875 1.575.875 2.717v5.365z" />
14+
</svg>
15+
)
16+
}

data/authors.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1+
export type AuthorLinkKind =
2+
| "website"
3+
| "linkedin"
4+
| "mastodon"
5+
| "launchpad"
6+
| "other";
7+
8+
export interface AuthorLink {
9+
label: string;
10+
href: string;
11+
kind?: AuthorLinkKind;
12+
}
13+
114
export interface Author {
215
key: string;
316
name: string;
417
role?: string;
518
location?: string;
619
email?: string;
720
github?: string;
21+
links?: AuthorLink[];
822
image?: string;
923
}
1024

@@ -17,6 +31,11 @@ const authors: Author[] = [
1731
location: "Vienna, Austria",
1832
email: "mailto:till.kamppeter@gmail.com",
1933
github: "https://github.com/tillkamppeter",
34+
links: [
35+
{ label: "LinkedIn", href: "https://linkedin.com/in/kamppetertill", kind: "linkedin" },
36+
{ label: "Mastodon", href: "https://ubuntu.social/@till", kind: "mastodon" },
37+
{ label: "Launchpad", href: "https://launchpad.net/~till-kamppeter", kind: "launchpad" },
38+
],
2039
image: "/authors/till-kamppeter.jpg",
2140
},
2241
{
@@ -25,6 +44,9 @@ const authors: Author[] = [
2544
role: "Author of CUPS and PAPPL",
2645
location: "Canada",
2746
github: "https://github.com/michaelrsweet",
47+
links: [
48+
{ label: "Website", href: "https://www.msweet.org/", kind: "website" },
49+
],
2850
image: "/authors/michael-sweet.jpg",
2951
},
3052
{
@@ -43,6 +65,9 @@ const authors: Author[] = [
4365
location: "Mandi, 175001, India",
4466
email: "mailto:dhirajyadav135@gmail.com",
4567
github: "https://github.com/dheeraj135",
68+
links: [
69+
{ label: "LinkedIn", href: "https://www.linkedin.com/in/dheeraj135/", kind: "linkedin" },
70+
],
4671
image: "/authors/dheeraj135.jpg",
4772
},
4873
{
@@ -60,6 +85,10 @@ const authors: Author[] = [
6085
location: "Campinas, Brazil",
6186
email: "mailto:carlosnsoliveira@gmail.com",
6287
github: "https://github.com/CarlosNihelton",
88+
links: [
89+
{ label: "LinkedIn", href: "https://linkedin.com/in/carlos-nihelton", kind: "linkedin" },
90+
{ label: "Launchpad", href: "https://launchpad.net/~cnihelton", kind: "launchpad" },
91+
],
6392
image: "/authors/cnihelton.jpg",
6493
},
6594
];

0 commit comments

Comments
 (0)