diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 134115b0e1..74c0dfb05b 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -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(); @@ -16,7 +16,7 @@ const { noMarginTop = false } = Astro.props;
- +
Copyright © {currentYear} diff --git a/src/components/ShareLinks.astro b/src/components/ShareLinks.astro deleted file mode 100644 index d65a0de1ea..0000000000 --- a/src/components/ShareLinks.astro +++ /dev/null @@ -1,26 +0,0 @@ ---- -import { SHARE_LINKS } from "@/constants"; -import LinkButton from "./LinkButton.astro"; - -const URL = Astro.url; ---- - -{ - SHARE_LINKS.length > 0 && ( -
- Share this post on: -
- {SHARE_LINKS.map(social => ( - - - {social.linkTitle} - - ))} -
-
- ) -} diff --git a/src/components/Socials.astro b/src/components/Socials.astro deleted file mode 100644 index 545fca7b5d..0000000000 --- a/src/components/Socials.astro +++ /dev/null @@ -1,25 +0,0 @@ ---- -import { SOCIALS } from "@/constants"; -import LinkButton from "./LinkButton.astro"; - -export interface Props { - centered?: boolean; -} - -const { centered = false } = Astro.props; ---- - -
- { - SOCIALS.map(social => ( - - - {social.linkTitle} - - )) - } -
diff --git a/src/components/constants/ShareLinks.astro b/src/components/constants/ShareLinks.astro new file mode 100644 index 0000000000..bd9bb370bd --- /dev/null +++ b/src/components/constants/ShareLinks.astro @@ -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 && ( +
+ Share this post on: +
+ {shareLinks.map(shareLink => ( + + + {shareLink.linkTitle} + + ))} +
+
+ ) +} diff --git a/src/components/constants/SocialLinks.astro b/src/components/constants/SocialLinks.astro new file mode 100644 index 0000000000..cda85bb413 --- /dev/null +++ b/src/components/constants/SocialLinks.astro @@ -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 && ( + + {socials.map(social => ( + + + {social.linkTitle} + + ))} + + ) +} diff --git a/src/components/constants/SocialLinksContainer.astro b/src/components/constants/SocialLinksContainer.astro new file mode 100644 index 0000000000..de026229a6 --- /dev/null +++ b/src/components/constants/SocialLinksContainer.astro @@ -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 ? ( +
+ +
+ ) : ( +
+
Social Links:
+
+ +
+
+ ) +} diff --git a/src/components/constants/_share.json b/src/components/constants/_share.json new file mode 100644 index 0000000000..0c82d1e40d --- /dev/null +++ b/src/components/constants/_share.json @@ -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" + } +] diff --git a/src/components/constants/_social.json b/src/components/constants/_social.json new file mode 100644 index 0000000000..4eb2ec8815 --- /dev/null +++ b/src/components/constants/_social.json @@ -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:yourmail@gmail.com", + "linkTitle": "Send an email to {title}" + } +] diff --git a/src/assets/icons/IconFacebook.svg b/src/components/constants/icons/facebook.svg similarity index 100% rename from src/assets/icons/IconFacebook.svg rename to src/components/constants/icons/facebook.svg diff --git a/src/assets/icons/IconGitHub.svg b/src/components/constants/icons/github.svg similarity index 100% rename from src/assets/icons/IconGitHub.svg rename to src/components/constants/icons/github.svg diff --git a/src/assets/icons/IconLinkedin.svg b/src/components/constants/icons/linkedin.svg similarity index 100% rename from src/assets/icons/IconLinkedin.svg rename to src/components/constants/icons/linkedin.svg diff --git a/src/assets/icons/IconMail.svg b/src/components/constants/icons/mail.svg similarity index 100% rename from src/assets/icons/IconMail.svg rename to src/components/constants/icons/mail.svg diff --git a/src/assets/icons/IconPinterest.svg b/src/components/constants/icons/pinterest.svg similarity index 100% rename from src/assets/icons/IconPinterest.svg rename to src/components/constants/icons/pinterest.svg diff --git a/src/assets/icons/IconTelegram.svg b/src/components/constants/icons/telegram.svg similarity index 100% rename from src/assets/icons/IconTelegram.svg rename to src/components/constants/icons/telegram.svg diff --git a/src/assets/icons/IconWhatsapp.svg b/src/components/constants/icons/whatsapp.svg similarity index 100% rename from src/assets/icons/IconWhatsapp.svg rename to src/components/constants/icons/whatsapp.svg diff --git a/src/assets/icons/IconBrandX.svg b/src/components/constants/icons/x.svg similarity index 100% rename from src/assets/icons/IconBrandX.svg rename to src/components/constants/icons/x.svg diff --git a/src/components/constants/utils.ts b/src/components/constants/utils.ts new file mode 100644 index 0000000000..b1b7a49037 --- /dev/null +++ b/src/components/constants/utils.ts @@ -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; + rendered?: RenderedContent; + filePath?: string; +} + +export async function getSocialIcons( + list: CollectionEntry[] +): Promise { + 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(() => { + 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; +} diff --git a/src/constants.ts b/src/constants.ts index a9004cdc31..65a16847f7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -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:yourmail@gmail.com", - 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"; diff --git a/src/content.config.ts b/src/content.config.ts index 6119aa62a8..310718515d 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -1,8 +1,7 @@ import { defineCollection, z } from "astro:content"; -import { glob } from "astro/loaders"; +import { file, glob } from "astro/loaders"; import { SITE } from "@/config"; - -export const BLOG_PATH = "src/data/blog"; +import { BLOG_PATH, SHARE_LINKS_PATH, SOCIALS_PATH } from "@/constants"; const blog = defineCollection({ loader: glob({ pattern: "**/[^_]*.md", base: `./${BLOG_PATH}` }), @@ -23,4 +22,20 @@ const blog = defineCollection({ }), }); -export const collections = { blog }; +const socialSchema = z.object({ + name: z.string(), + href: z.string(), + linkTitle: z.string(), +}); + +const socials = defineCollection({ + loader: file(`./${SOCIALS_PATH}`), + schema: socialSchema, +}); + +const shareLinks = defineCollection({ + loader: file(`./${SHARE_LINKS_PATH}`), + schema: socialSchema, +}); + +export const collections = { blog, socials, shareLinks }; diff --git a/src/layouts/PostDetails.astro b/src/layouts/PostDetails.astro index 23def5d28e..9a7036627b 100644 --- a/src/layouts/PostDetails.astro +++ b/src/layouts/PostDetails.astro @@ -6,7 +6,7 @@ import Footer from "@/components/Footer.astro"; import Tag from "@/components/Tag.astro"; import Datetime from "@/components/Datetime.astro"; import EditPost from "@/components/EditPost.astro"; -import ShareLinks from "@/components/ShareLinks.astro"; +import ShareLinks from "@/components/constants/ShareLinks.astro"; import BackButton from "@/components/BackButton.astro"; import BackToTopButton from "@/components/BackToTopButton.astro"; import { getPath } from "@/utils/getPath"; diff --git a/src/pages/index.astro b/src/pages/index.astro index 8f0ce93653..c776b6858c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -3,15 +3,14 @@ import { getCollection } from "astro:content"; import Layout from "@/layouts/Layout.astro"; import Header from "@/components/Header.astro"; import Footer from "@/components/Footer.astro"; -import Socials from "@/components/Socials.astro"; -import LinkButton from "@/components/LinkButton.astro"; -import Card from "@/components/Card.astro"; import Hr from "@/components/Hr.astro"; +import Card from "@/components/Card.astro"; +import LinkButton from "@/components/LinkButton.astro"; +import SocialsLinks from "@/components/constants/SocialLinks.astro"; import getSortedPosts from "@/utils/getSortedPosts"; import IconRss from "@/assets/icons/IconRss.svg"; import IconArrowRight from "@/assets/icons/IconArrowRight.svg"; import { SITE } from "@/config"; -import { SOCIALS } from "@/constants"; const posts = await getCollection("blog"); @@ -57,15 +56,7 @@ const recentPosts = sortedPosts.filter(({ data }) => !data.featured); README for more info.

- { - // only display if at least one social link is enabled - SOCIALS.length > 0 && ( -
-
Social Links:
- -
- ) - } +
diff --git a/src/utils/getPath.ts b/src/utils/getPath.ts index 057b612fe1..5172597f48 100644 --- a/src/utils/getPath.ts +++ b/src/utils/getPath.ts @@ -1,4 +1,4 @@ -import { BLOG_PATH } from "@/content.config"; +import { BLOG_PATH } from "@/constants"; import { slugifyStr } from "./slugify"; /**