Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/Footer.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import SocialLinks from "./constants/SocialLinks.astro";
import Hr from "./Hr.astro";
import Socials from "./Socials.astro";

const currentYear = new Date().getFullYear();

Expand All @@ -16,7 +16,7 @@ const { noMarginTop = false } = Astro.props;
<div
class="flex flex-col items-center justify-between py-6 sm:flex-row-reverse sm:py-4"
>
<Socials centered />
<SocialLinks minimal />
<div class="my-2 flex flex-col items-center whitespace-nowrap sm:flex-row">
<span>Copyright &#169; {currentYear}</span>
<span class="hidden sm:inline">&nbsp;|&nbsp;</span>
Expand Down
26 changes: 0 additions & 26 deletions src/components/ShareLinks.astro

This file was deleted.

25 changes: 0 additions & 25 deletions src/components/Socials.astro

This file was deleted.

31 changes: 31 additions & 0 deletions src/components/constants/ShareLinks.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import LinkButton from "@/components/LinkButton.astro";
import { getCollection } from "astro:content";
import { getSocialIcons } from "./utils";

const shareLinkCollection = await getCollection("shareLinks");

const shareLinks = await getSocialIcons(shareLinkCollection);

const URL = Astro.url;
---

{
shareLinks.length > 0 && (
<div class="flex flex-none flex-col items-center justify-center gap-1 md:items-start">
<span class="italic">Share this post on:</span>
<div class="text-center">
{shareLinks.map(shareLink => (
<LinkButton
href={`${shareLink.href + URL}`}
class="scale-90 p-2 hover:rotate-6 sm:p-1"
title={shareLink.linkTitle}
>
<shareLink.icon class="inline-block size-6 scale-125 fill-transparent stroke-current stroke-2 opacity-90 group-hover:fill-transparent sm:scale-110" />
<span class="sr-only">{shareLink.linkTitle}</span>
</LinkButton>
))}
</div>
</div>
)
}
37 changes: 37 additions & 0 deletions src/components/constants/SocialLinks.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
import { getCollection } from "astro:content";
import LinkButton from "@/components/LinkButton.astro";
import { getSocialIcons } from "./utils";
import SocialLinksContainer from "./SocialLinksContainer.astro";

const socialsCollection = await getCollection("socials");
const socials = await getSocialIcons(socialsCollection);

export interface Props {
minimal?: boolean;
size?: number;
}

const { minimal = false, size = 26 } = Astro.props;
---

{
socials.length > 0 && (
<SocialLinksContainer minimal={minimal}>
{socials.map(social => (
<LinkButton
href={social.href}
class="p-2 hover:rotate-6 sm:p-1"
title={social.linkTitle}
>
<social.icon
height={size}
width={size}
class="inline-block size-6 fill-transparent stroke-current stroke-2 opacity-90 group-hover:fill-transparent max-sm:scale-110"
/>
<span class="sr-only">{social.linkTitle}</span>
</LinkButton>
))}
</SocialLinksContainer>
)
}
24 changes: 24 additions & 0 deletions src/components/constants/SocialLinksContainer.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
export interface Props {
minimal?: boolean;
}

const { minimal = false } = Astro.props;

const containerClass = ["flex-wrap justify-center gap-1", { flex: minimal }];
---

{
minimal ? (
<div class:list={containerClass}>
<slot />
</div>
) : (
<div class="mt-4 flex flex-col sm:flex-row sm:items-center">
<div class="me-2 mb-1 whitespace-nowrap sm:mb-0">Social Links:</div>
<div class:list={containerClass}>
<slot />
</div>
</div>
)
}
38 changes: 38 additions & 0 deletions src/components/constants/_share.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"id": "whatsapp",
"name": "WhatsApp",
"href": "https://wa.me/?text=",
"linkTitle": "Share this post via WhatsApp"
},
{
"id": "facebook",
"name": "Facebook",
"href": "https://www.facebook.com/sharer.php?u=",
"linkTitle": "Share this post on Facebook"
},
{
"id": "x",
"name": "X",
"href": "https://x.com/intent/post?url=",
"linkTitle": "Share this post on X"
},
{
"id": "telegram",
"name": "Telegram",
"href": "https://t.me/share/url?url=",
"linkTitle": "Share this post via Telegram"
},
{
"id": "pinterest",
"name": "Pinterest",
"href": "https://pinterest.com/pin/create/button/?url=",
"linkTitle": "Share this post on Pinterest"
},
{
"id": "mail",
"name": "Mail",
"href": "mailto:?subject=See%20this%20post&body=",
"linkTitle": "Share this post via email"
}
]
26 changes: 26 additions & 0 deletions src/components/constants/_social.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"id": "github",
"name": "GitHub",
"href": "https://github.com/satnaing/astro-paper",
"linkTitle": "{title} on GitHub"
},
{
"id": "x",
"name": "X",
"href": "https://x.com/username",
"linkTitle": "{title} on X"
},
{
"id": "linkedin",
"name": "LinkedIn",
"href": "https://www.linkedin.com/in/username/",
"linkTitle": "{title} on LinkedIn"
},
{
"id": "mail",
"name": "Mail",
"href": "mailto:[email protected]",
"linkTitle": "Send an email to {title}"
}
]
File renamed without changes
52 changes: 52 additions & 0 deletions src/components/constants/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Props } from "astro";
import type { InferEntrySchema, RenderedContent } from "astro:content";
import { SITE } from "@/config";

export interface Social {
name: string;
href: string;
linkTitle: string;
icon: (_props: Props) => Element;
}

type CollectionKey = "socials" | "shareLinks";

interface CollectionEntry {
id: string;
body?: string;
collection: CollectionKey;
data: InferEntrySchema<CollectionKey>;
rendered?: RenderedContent;
filePath?: string;
}

export async function getSocialIcons(
list: CollectionEntry[]
): Promise<Social[]> {
const socials: Social[] = [];

for (const { id, data } of list) {
// Import the icon dynamically based on the social id
const icon = await import(`./icons/${id}.svg`).catch(() => {
Copy link
Contributor

@eerison eerison Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @satnaing

Would it be possible to create a function that return all available icons?

Or maybe a json with this list 👀
Or maybe generate a json file on build/command line

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RN, we can get both shareLinks and socials using getSocialIcons().
I cannot think where we would need that functionality tho.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah in Astropaper it won't be really usefull, But when we use some admin to handle md files, then it could help.
I am using decapcms and I was think to add a select field with all avalaible icons, then select the icon on admin.

if (import.meta.env.DEV) {
throw new Error(
`[Socials]: Icon "${id}" not found. Please ensure the icon exists in the src/components/constants/icons directory.`
);
}
return null;
});

// Skip if icon import failed
if (!icon) {
continue;
}

socials.push({
...data,
icon: icon.default,
linkTitle: data.linkTitle.replace("{title}", SITE.title),
});
}

return socials;
}
86 changes: 3 additions & 83 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,3 @@
import type { Props } from "astro";
import IconMail from "@/assets/icons/IconMail.svg";
import IconGitHub from "@/assets/icons/IconGitHub.svg";
import IconBrandX from "@/assets/icons/IconBrandX.svg";
import IconLinkedin from "@/assets/icons/IconLinkedin.svg";
import IconWhatsapp from "@/assets/icons/IconWhatsapp.svg";
import IconFacebook from "@/assets/icons/IconFacebook.svg";
import IconTelegram from "@/assets/icons/IconTelegram.svg";
import IconPinterest from "@/assets/icons/IconPinterest.svg";
import { SITE } from "@/config";

interface Social {
name: string;
href: string;
linkTitle: string;
icon: (_props: Props) => Element;
}

export const SOCIALS: Social[] = [
{
name: "GitHub",
href: "https://github.com/satnaing/astro-paper",
linkTitle: `${SITE.title} on GitHub`,
icon: IconGitHub,
},
{
name: "X",
href: "https://x.com/username",
linkTitle: `${SITE.title} on X`,
icon: IconBrandX,
},
{
name: "LinkedIn",
href: "https://www.linkedin.com/in/username/",
linkTitle: `${SITE.title} on LinkedIn`,
icon: IconLinkedin,
},
{
name: "Mail",
href: "mailto:[email protected]",
linkTitle: `Send an email to ${SITE.title}`,
icon: IconMail,
},
] as const;

export const SHARE_LINKS: Social[] = [
{
name: "WhatsApp",
href: "https://wa.me/?text=",
linkTitle: `Share this post via WhatsApp`,
icon: IconWhatsapp,
},
{
name: "Facebook",
href: "https://www.facebook.com/sharer.php?u=",
linkTitle: `Share this post on Facebook`,
icon: IconFacebook,
},
{
name: "X",
href: "https://x.com/intent/post?url=",
linkTitle: `Share this post on X`,
icon: IconBrandX,
},
{
name: "Telegram",
href: "https://t.me/share/url?url=",
linkTitle: `Share this post via Telegram`,
icon: IconTelegram,
},
{
name: "Pinterest",
href: "https://pinterest.com/pin/create/button/?url=",
linkTitle: `Share this post on Pinterest`,
icon: IconPinterest,
},
{
name: "Mail",
href: "mailto:?subject=See%20this%20post&body=",
linkTitle: `Share this post via email`,
icon: IconMail,
},
] as const;
export const BLOG_PATH = "src/data/blog";
export const SHARE_LINKS_PATH = "src/components/constants/_share.json";
export const SOCIALS_PATH = "src/components/constants/_social.json";
Loading