diff options
| author | Dawid Rycerz <dawid@rycerz.xyz> | 2026-01-13 20:06:15 +0100 |
|---|---|---|
| committer | Dawid Rycerz <dawid@rycerz.xyz> | 2026-01-13 20:06:15 +0100 |
| commit | b6e440699e9fca474869bf74ce09f2310f05c620 (patch) | |
| tree | a089f456a0e9ebb6f9f6b72370deb7eeb8a3e308 | |
| parent | f8a4e00f89913b0c57ca016965f49efc26f4bff9 (diff) | |
Cleanup unused functionalities
| -rw-r--r-- | astro.config.ts | 15 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 8 | ||||
| -rw-r--r-- | src/components/BaseHead.astro | 11 | ||||
| -rw-r--r-- | src/components/blog/Masthead.astro | 7 | ||||
| -rw-r--r-- | src/components/blog/webmentions/Comments.astro | 90 | ||||
| -rw-r--r-- | src/components/blog/webmentions/Likes.astro | 54 | ||||
| -rw-r--r-- | src/components/blog/webmentions/index.astro | 29 | ||||
| -rw-r--r-- | src/i18n/translations.ts | 14 | ||||
| -rw-r--r-- | src/layouts/BlogPost.astro | 8 | ||||
| -rw-r--r-- | src/plugins/remark-reading-time.ts | 11 | ||||
| -rw-r--r-- | src/types.ts | 56 | ||||
| -rw-r--r-- | src/utils/webmentions.ts | 115 |
13 files changed, 5 insertions, 414 deletions
diff --git a/astro.config.ts b/astro.config.ts index cf262a0..792a5f2 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -4,7 +4,7 @@ import { rehypeHeadingIds } from "@astrojs/markdown-remark"; import mdx from "@astrojs/mdx"; import sitemap from "@astrojs/sitemap"; import tailwind from "@tailwindcss/vite"; -import { defineConfig, envField } from "astro/config"; +import { defineConfig } from "astro/config"; import expressiveCode from "astro-expressive-code"; import icon from "astro-icon"; import robotsTxt from "astro-robots-txt"; @@ -16,15 +16,11 @@ import rehypeUnwrapImages from "rehype-unwrap-images"; import remarkDirective from "remark-directive"; /* Handle ::: directives as nodes */ import robotsConfig from "./robots-txt.config"; import { remarkAdmonitions } from "./src/plugins/remark-admonitions"; /* Add admonitions */ -import { remarkReadingTime } from "./src/plugins/remark-reading-time"; import { expressiveCodeOptions, siteConfig } from "./src/site.config"; // https://astro.build/config export default defineConfig({ site: siteConfig.url, - image: { - domains: ["webmention.io"], - }, integrations: [ expressiveCode(expressiveCodeOptions), icon(), @@ -79,7 +75,7 @@ export default defineConfig({ ], rehypeUnwrapImages, ], - remarkPlugins: [remarkReadingTime, remarkDirective, remarkAdmonitions], + remarkPlugins: [remarkDirective, remarkAdmonitions], remarkRehype: { footnoteLabelProperties: { className: [""], @@ -94,13 +90,6 @@ export default defineConfig({ }, plugins: [tailwind(), rawFonts([".ttf", ".woff"])], }, - env: { - schema: { - WEBMENTION_API_KEY: envField.string({ context: "server", access: "secret", optional: true }), - WEBMENTION_URL: envField.string({ context: "client", access: "public", optional: true }), - WEBMENTION_PINGBACK: envField.string({ context: "client", access: "public", optional: true }), - }, - }, }); function rawFonts(ext: string[]) { diff --git a/package.json b/package.json index 09ccde9..b1fdb22 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "prettier": "^3.6.2", "prettier-plugin-astro": "0.14.1", "prettier-plugin-tailwindcss": "^0.6.13", - "reading-time": "^1.5.0", "tailwindcss": "4.1.11", "typescript": "^5.8.3", "wrangler": "^4.59.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 395f4ea..2e47431 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -144,9 +144,6 @@ importers: prettier-plugin-tailwindcss: specifier: ^0.6.13 version: 0.6.13(prettier-plugin-astro@0.14.1)(prettier@3.6.2) - reading-time: - specifier: ^1.5.0 - version: 1.5.0 tailwindcss: specifier: 4.1.11 version: 4.1.11 @@ -2984,9 +2981,6 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - reading-time@1.5.0: - resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==} - recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -6869,8 +6863,6 @@ snapshots: readdirp@4.1.2: {} - reading-time@1.5.0: {} - recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.7 diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro index 14286c3..2e5a9cd 100644 --- a/src/components/BaseHead.astro +++ b/src/components/BaseHead.astro @@ -1,5 +1,4 @@ --- -import { WEBMENTION_PINGBACK, WEBMENTION_URL } from "astro:env/client"; import { siteConfig } from "@/site.config"; import type { SiteMeta } from "@/types"; import "@/styles/global.css"; @@ -96,16 +95,6 @@ const ogLocale = getOgLocale(lang); {/* RSS auto-discovery */} <link href="/rss.xml" title="Blog" rel="alternate" type="application/rss+xml" /> -{/* Webmentions */} -{ - WEBMENTION_URL && ( - <> - <link href={WEBMENTION_URL} rel="webmention" /> - {WEBMENTION_PINGBACK && <link href={WEBMENTION_PINGBACK} rel="pingback" />} - </> - ) -} - {/* Plausible Analytics */} <link rel="dns-prefetch" href="//analytics.craftknight.com" /> <script diff --git a/src/components/blog/Masthead.astro b/src/components/blog/Masthead.astro index e7b3ea9..7a50d8a 100644 --- a/src/components/blog/Masthead.astro +++ b/src/components/blog/Masthead.astro @@ -6,13 +6,11 @@ import { t } from "@/i18n/translations"; interface Props { content: CollectionEntry<"post">; - readingTime: string; language?: string | undefined; } const { content: { data }, - readingTime, language, } = Astro.props; @@ -41,10 +39,7 @@ const dateTimeOptions: Intl.DateTimeFormatOptions = { </h1> <div class="flex flex-wrap items-center gap-x-3 gap-y-2"> <p class="font-semibold"> - <FormattedDate date={data.publishDate} dateTimeOptions={dateTimeOptions} locale={language} /> /{ - " " - } - {readingTime} + <FormattedDate date={data.publishDate} dateTimeOptions={dateTimeOptions} locale={language} /> </p> { data.updatedDate && ( diff --git a/src/components/blog/webmentions/Comments.astro b/src/components/blog/webmentions/Comments.astro deleted file mode 100644 index 9d274f9..0000000 --- a/src/components/blog/webmentions/Comments.astro +++ /dev/null @@ -1,90 +0,0 @@ ---- -import { Image } from "astro:assets"; -import { Icon } from "astro-icon/components"; -import { t } from "@/i18n/translations"; -import type { WebmentionsChildren } from "@/types"; - -interface Props { - mentions: WebmentionsChildren[]; - language?: string | undefined; -} - -const { mentions, language } = Astro.props; - -const validComments = ["mention-of", "in-reply-to"]; - -const comments = mentions.filter( - (mention) => validComments.includes(mention["wm-property"]) && mention.content?.text, -); ---- - -{ - !!comments.length && ( - <div> - <p class="text-accent-2 mb-0"> - <strong>{comments.length}</strong>{" "} - {comments.length > 1 ? t(language, "mentions") : t(language, "mention")} - </p> - <ul class="divide-global-text/20 mt-0 divide-y ps-0" role="list"> - {comments.map((mention) => ( - <li class="p-comment h-cite my-0 flex items-start gap-x-5 py-5"> - {mention.author?.photo && mention.author.photo !== "" ? ( - mention.author.url && mention.author.url !== "" ? ( - <a - class="u-author not-prose ring-global-text hover:ring-link focus-visible:ring-link shrink-0 overflow-hidden rounded-full ring-2 hover:ring-4 focus-visible:ring-4" - href={mention.author.url} - rel="noreferrer" - target="_blank" - title={mention.author.name} - > - <Image - alt={mention.author?.name} - class="u-photo my-0 h-12 w-12" - height={48} - src={mention.author?.photo} - width={48} - /> - </a> - ) : ( - <Image - alt={mention.author?.name} - class="u-photo my-0 h-12 w-12 rounded-full" - height={48} - src={mention.author?.photo} - width={48} - /> - ) - ) : null} - <div class="flex-auto"> - <div class="p-author h-card flex items-center justify-between gap-x-2"> - <p class="p-name text-accent-2 my-0 line-clamp-1 font-semibold"> - {mention.author?.name} - </p> - <a - aria-labelledby="cmt-source" - class="u-url not-prose hover:text-link" - href={mention.url} - rel="noreferrer" - target="_blank" - > - <span class="hidden" id="cmt-source"> - {t(language, "visitWebmentionSource")} - </span> - <Icon - aria-hidden="true" - class="h-5 w-5" - focusable="false" - name="mdi:open-in-new" - /> - </a> - </div> - <p class="comment-content mt-1 mb-0 break-words [word-break:break-word]"> - {mention.content?.text} - </p> - </div> - </li> - ))} - </ul> - </div> - ) -} diff --git a/src/components/blog/webmentions/Likes.astro b/src/components/blog/webmentions/Likes.astro deleted file mode 100644 index fce1a96..0000000 --- a/src/components/blog/webmentions/Likes.astro +++ /dev/null @@ -1,54 +0,0 @@ ---- -import { Image } from "astro:assets"; -import { t } from "@/i18n/translations"; -import type { WebmentionsChildren } from "@/types"; - -interface Props { - mentions: WebmentionsChildren[]; - language?: string | undefined; -} - -const { mentions, language } = Astro.props; -const MAX_LIKES = 10; - -const likes = mentions.filter((mention) => mention["wm-property"] === "like-of"); -const likesToShow = likes - .filter((like) => like.author?.photo && like.author.photo !== "") - .slice(0, MAX_LIKES); ---- - -{ - !!likes.length && ( - <div> - <p class="text-accent-2 mb-0"> - <strong>{likes.length}</strong>{" "} - {likes.length > 1 ? t(language, "peopleLiked") : t(language, "personLiked")} - </p> - {!!likesToShow.length && ( - <ul class="flex list-none flex-wrap overflow-hidden ps-2" role="list"> - {likesToShow.map((like) => ( - <li class="p-like h-cite -ms-2"> - <a - class="u-url not-prose ring-global-text hover:ring-link focus-visible:ring-link relative inline-block overflow-hidden rounded-full ring-2 hover:z-10 hover:ring-4 focus-visible:z-10 focus-visible:ring-4" - href={like.author?.url} - rel="noreferrer" - target="_blank" - title={like.author?.name} - > - <span class="p-author h-card"> - <Image - alt={like.author!.name} - class="u-photo my-0 inline-block h-12 w-12" - height={48} - src={like.author!.photo} - width={48} - /> - </span> - </a> - </li> - ))} - </ul> - )} - </div> - ) -} diff --git a/src/components/blog/webmentions/index.astro b/src/components/blog/webmentions/index.astro deleted file mode 100644 index 80376f0..0000000 --- a/src/components/blog/webmentions/index.astro +++ /dev/null @@ -1,29 +0,0 @@ ---- -import { t } from "@/i18n/translations"; -import { getWebmentionsForUrl } from "@/utils/webmentions"; -import Comments from "./Comments.astro"; -import Likes from "./Likes.astro"; - -interface Props { - language?: string | undefined; -} - -const { language } = Astro.props; -const url = new URL(Astro.url.pathname, Astro.site); - -const webMentions = await getWebmentionsForUrl(`${url}`); - -// Return if no webmentions -if (!webMentions.length) return; ---- - -<hr class="border-solid" /> -<h2 class="mb-8 before:hidden">{t(language, "webmentionsTitle")}</h2> -<div class="space-y-10"> - <Likes mentions={webMentions} language={language} /> - <Comments mentions={webMentions} language={language} /> -</div> -<p class="mt-8"> - {t(language, "responsesPoweredBy")}{" "} - <a href="https://webmention.io" rel="noreferrer" target="_blank">Webmentions</a> -</p> diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 7631ad7..b68893a 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -4,26 +4,12 @@ export const translations = { backToTop: "Back to top", updated: "Updated:", viewMoreWithTag: "View more blogs with the tag", - webmentionsTitle: "Webmentions for this post", - responsesPoweredBy: "Responses powered by", - mention: "Mention", - mentions: "Mentions", - visitWebmentionSource: "Visit the source of this webmention", - personLiked: "Person liked this", - peopleLiked: "People liked this", }, pl: { viewOriginalPost: "Zobacz oryginalny wpis na Pleroma →", backToTop: "Powrót na górę", updated: "Zaktualizowano:", viewMoreWithTag: "Zobacz więcej wpisów z tagiem", - webmentionsTitle: "Webmentions dla tego wpisu", - responsesPoweredBy: "Odpowiedzi dzięki", - mention: "Wzmianka", - mentions: "Wzmianki", - visitWebmentionSource: "Odwiedź źródło tej wzmianki", - personLiked: "osoba polubiła", - peopleLiked: "osób polubiło", }, } as const; diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro index 436efde..764cd80 100644 --- a/src/layouts/BlogPost.astro +++ b/src/layouts/BlogPost.astro @@ -1,8 +1,7 @@ --- -import { type CollectionEntry, render } from "astro:content"; +import type { CollectionEntry } from "astro:content"; import Masthead from "@/components/blog/Masthead.astro"; -import WebMentions from "@/components/blog/webmentions/index.astro"; import { t } from "@/i18n/translations"; import BaseLayout from "./Base.astro"; @@ -15,8 +14,6 @@ const { post } = Astro.props; const { ogImage, title, description, updatedDate, publishDate, language } = post.data; const socialImage = ogImage ?? `/og-image/${post.id}.png`; const articleDate = updatedDate?.toISOString() ?? publishDate.toISOString(); -const { remarkPluginFrontmatter } = await render(post); -const readingTime: string = remarkPluginFrontmatter.readingTime; --- <BaseLayout @@ -30,13 +27,12 @@ const readingTime: string = remarkPluginFrontmatter.readingTime; > <article class="grow break-words" data-pagefind-body> <div id="blog-hero" class="mb-12"> - <Masthead content={post} readingTime={readingTime} language={language} /> + <Masthead content={post} language={language} /> </div> <div class="prose prose-sm prose-cactus prose-headings:font-semibold prose-headings:text-accent-2 prose-headings:before:absolute prose-headings:before:-ms-4 prose-headings:before:text-gray-600 prose-headings:hover:before:text-accent sm:prose-headings:before:content-['#'] sm:prose-th:before:content-none max-w-none" > <slot /> - <WebMentions language={language} /> { post.data.sourceUrl && ( <p class="mt-8 border-t border-gray-200 pt-6 text-sm dark:border-gray-700"> diff --git a/src/plugins/remark-reading-time.ts b/src/plugins/remark-reading-time.ts deleted file mode 100644 index 843dde1..0000000 --- a/src/plugins/remark-reading-time.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { toString as mdastToString } from "mdast-util-to-string"; -import getReadingTime from "reading-time"; - -export function remarkReadingTime() { - // @ts-expect-error:next-line - return (tree, { data }) => { - const textOnPage = mdastToString(tree); - const readingTime = getReadingTime(textOnPage); - data.astro.frontmatter.readingTime = readingTime.text; - }; -} diff --git a/src/types.ts b/src/types.ts index ec40902..4177fb1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,60 +25,4 @@ export interface SiteMeta { lang?: string | undefined; } -/** Webmentions */ -export interface WebmentionsFeed { - children: WebmentionsChildren[]; - name: string; - type: string; -} - -export interface WebmentionsCache { - children: WebmentionsChildren[]; - lastFetched: null | string; -} - -export interface WebmentionsChildren { - author: Author | null; - content?: Content | null; - "mention-of": string; - name?: null | string; - photo?: null | string[]; - published?: null | string; - rels?: Rels | null; - summary?: Summary | null; - syndication?: null | string[]; - type: string; - url: string; - "wm-id": number; - "wm-private": boolean; - "wm-property": string; - "wm-protocol": string; - "wm-received": string; - "wm-source": string; - "wm-target": string; -} - -export interface Author { - name: string; - photo: string; - type: string; - url: string; -} - -export interface Content { - "content-type": string; - html: string; - text: string; - value: string; -} - -export interface Rels { - canonical: string; -} - -export interface Summary { - "content-type": string; - value: string; -} - export type AdmonitionType = "tip" | "note" | "important" | "caution" | "warning"; diff --git a/src/utils/webmentions.ts b/src/utils/webmentions.ts deleted file mode 100644 index 8edfd90..0000000 --- a/src/utils/webmentions.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { WEBMENTION_API_KEY } from "astro:env/server"; -import * as fs from "node:fs"; -import type { WebmentionsCache, WebmentionsChildren, WebmentionsFeed } from "@/types"; - -const DOMAIN = import.meta.env.SITE; -const CACHE_DIR = ".data"; -const filePath = `${CACHE_DIR}/webmentions.json`; -const validWebmentionTypes = ["like-of", "mention-of", "in-reply-to"]; - -const hostName = new URL(DOMAIN).hostname; - -// Calls webmention.io api. -async function fetchWebmentions(timeFrom: string | null, perPage = 1000) { - if (!DOMAIN) { - console.warn("No domain specified. Please set in astro.config.ts"); - return null; - } - - if (!WEBMENTION_API_KEY) { - console.warn("No webmention api token specified in .env"); - return null; - } - - let url = `https://webmention.io/api/mentions.jf2?domain=${hostName}&token=${WEBMENTION_API_KEY}&sort-dir=up&per-page=${perPage}`; - - if (timeFrom) url += `&since${timeFrom}`; - - const res = await fetch(url); - - if (res.ok) { - const data = (await res.json()) as WebmentionsFeed; - return data; - } - - return null; -} - -// Merge cached entries [a] with fresh webmentions [b], merge by wm-id -function mergeWebmentions(a: WebmentionsCache, b: WebmentionsFeed): WebmentionsChildren[] { - return Array.from( - [...a.children, ...b.children] - .reduce((map, obj) => map.set(obj["wm-id"], obj), new Map()) - .values(), - ); -} - -// filter out WebmentionChildren -export function filterWebmentions(webmentions: WebmentionsChildren[]) { - return webmentions.filter((webmention) => { - // make sure the mention has a property so we can sort them later - if (!validWebmentionTypes.includes(webmention["wm-property"])) return false; - - // make sure 'mention-of' or 'in-reply-to' has text content. - if (webmention["wm-property"] === "mention-of" || webmention["wm-property"] === "in-reply-to") { - return webmention.content && webmention.content.text !== ""; - } - - return true; - }); -} - -// save combined webmentions in cache file -function writeToCache(data: WebmentionsCache) { - const fileContent = JSON.stringify(data, null, 2); - - // create cache folder if it doesn't exist already - if (!fs.existsSync(CACHE_DIR)) { - fs.mkdirSync(CACHE_DIR); - } - - // write data to cache json file - fs.writeFile(filePath, fileContent, (err) => { - if (err) throw err; - console.log(`Webmentions saved to ${filePath}`); - }); -} - -function getFromCache(): WebmentionsCache { - if (fs.existsSync(filePath)) { - const data = fs.readFileSync(filePath, "utf-8"); - return JSON.parse(data); - } - // no cache found - return { - lastFetched: null, - children: [], - }; -} - -async function getAndCacheWebmentions() { - const cache = getFromCache(); - const mentions = await fetchWebmentions(cache.lastFetched); - - if (mentions) { - mentions.children = filterWebmentions(mentions.children); - const webmentions: WebmentionsCache = { - lastFetched: new Date().toISOString(), - // Make sure the first arg is the cache - children: mergeWebmentions(cache, mentions), - }; - - writeToCache(webmentions); - return webmentions; - } - - return cache; -} - -let webMentions: WebmentionsCache; - -export async function getWebmentionsForUrl(url: string) { - if (!webMentions) webMentions = await getAndCacheWebmentions(); - - return webMentions.children.filter((entry) => entry["wm-target"] === url); -} |
