diff --git a/.env.example b/.env.example index 1563be05..0eb848a2 100644 --- a/.env.example +++ b/.env.example @@ -38,3 +38,8 @@ RESEND_DOMAIN= NEXT_PUBLIC_TURNSTILE_SITE_KEY= # used for backend validation of turnstile result. TURNSTILE_SECRET_KEY= + +# optional. sets "Onion-Location" response header to advertise a hidden service for the site; browsers like Brave and +# Tor Browser will automatically pick this up and offer to redirect users to it. +# https://community.torproject.org/onion-services/advanced/onion-location/ +NEXT_PUBLIC_ONION_DOMAIN= diff --git a/app/contact/actions.ts b/app/contact/actions.ts index 548c4a79..324492e9 100644 --- a/app/contact/actions.ts +++ b/app/contact/actions.ts @@ -54,9 +54,9 @@ export const sendMessage = async ( throw new Error(`[contact form] turnstile validation failed: ${turnstileResponse.status}`); } - const turnstileData = await turnstileResponse?.json(); + const turnstileData = (await turnstileResponse.json()) as { success: boolean }; - if (!turnstileData?.success) { + if (!turnstileData.success) { return { success: false, message: "Did you complete the CAPTCHA? (If you're human, that is...)", diff --git a/app/notes/[slug]/opengraph-image.tsx b/app/notes/[slug]/opengraph-image.tsx index 2b252c52..199b6a4f 100644 --- a/app/notes/[slug]/opengraph-image.tsx +++ b/app/notes/[slug]/opengraph-image.tsx @@ -54,7 +54,7 @@ const Image = async ({ params }: { params: Promise<{ slug: string }> }) => { const { slug } = await params; // get the post's title and image filename from its frontmatter - const { title, image: imagePath } = await getFrontMatter(slug); + const frontmatter = await getFrontMatter(slug); return new ImageResponse( ( @@ -67,7 +67,7 @@ const Image = async ({ params }: { params: Promise<{ slug: string }> }) => { background: "linear-gradient(0deg, hsla(197, 14%, 57%, 1) 0%, hsla(192, 17%, 94%, 1) 100%)", }} > - {imagePath && ( + {frontmatter!.image && (
}) => { {/* eslint-disable-next-line jsx-a11y/alt-text */}
@@ -117,7 +117,7 @@ const Image = async ({ params }: { params: Promise<{ slug: string }> }) => { color: "#fefefe", }} > - {title} + {frontmatter!.title} ), diff --git a/app/notes/[slug]/page.module.css b/app/notes/[slug]/page.module.css index 98091860..28e8bab3 100644 --- a/app/notes/[slug]/page.module.css +++ b/app/notes/[slug]/page.module.css @@ -65,7 +65,7 @@ margin-top: 2em; padding-top: 2em; border-top: 2px solid var(--colors-light); - min-height: 360px; + min-height: 140px; } @media (max-width: 768px) { diff --git a/app/notes/[slug]/page.tsx b/app/notes/[slug]/page.tsx index de606f9d..e3660070 100644 --- a/app/notes/[slug]/page.tsx +++ b/app/notes/[slug]/page.tsx @@ -37,14 +37,14 @@ export const generateMetadata = async ({ params }: { params: Promise<{ slug: str const frontmatter = await getFrontMatter(slug); return addMetadata({ - title: frontmatter.title, - description: frontmatter.description, + title: frontmatter!.title, + description: frontmatter!.description, openGraph: { type: "article", authors: [config.authorName], - tags: frontmatter.tags, - publishedTime: frontmatter.date, - modifiedTime: frontmatter.date, + tags: frontmatter!.tags, + publishedTime: frontmatter!.date, + modifiedTime: frontmatter!.date, }, twitter: { card: "summary_large_image", @@ -67,13 +67,13 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => { item={{ "@context": "https://schema.org", "@type": "Article", - headline: frontmatter.title, - description: frontmatter.description, - url: frontmatter.permalink, + headline: frontmatter!.title, + description: frontmatter!.description, + url: frontmatter!.permalink, image: [`${BASE_URL}/notes/${slug}/opengraph-image`], - keywords: frontmatter.tags, - datePublished: frontmatter.date, - dateModified: frontmatter.date, + keywords: frontmatter!.tags, + datePublished: frontmatter!.date, + dateModified: frontmatter!.date, inLanguage: config.siteLocale, license: config.licenseUrl, author: { @@ -85,17 +85,17 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
- + -
- {frontmatter.tags && ( + {frontmatter!.tags && (
- {frontmatter.tags.map((tag) => ( + {frontmatter!.tags.map((tag) => ( {tag} @@ -106,8 +106,8 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
@@ -129,7 +129,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => { > }> - +
@@ -138,8 +138,8 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {

@@ -147,9 +147,11 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => { - {!frontmatter.noComments && ( + {!frontmatter!.noComments && (
- + }> + +
)} diff --git a/components/Comments/Comments.tsx b/components/Comments/Comments.tsx index 91d4deaf..0267cb6a 100644 --- a/components/Comments/Comments.tsx +++ b/components/Comments/Comments.tsx @@ -18,7 +18,6 @@ const Comments = ({ title }: CommentsProps) => { return null; } - // TODO: use custom `` spinner component during suspense return ( { if (!width) return MAX_WIDTH; + // ensure that the image width is not greater than the global maximum width return Math.min(typeof width === "string" ? parseInt(width, 10) : width, MAX_WIDTH); }; diff --git a/components/RelativeTime/RelativeTime.tsx b/components/RelativeTime/RelativeTime.tsx index f8d6bdb9..4945831c 100644 --- a/components/RelativeTime/RelativeTime.tsx +++ b/components/RelativeTime/RelativeTime.tsx @@ -1,11 +1,8 @@ "use client"; +import TimeAgo from "react-timeago"; +import Time from "../Time"; import { useHasMounted } from "../../hooks"; -import { format, formatISO, formatDistanceToNowStrict } from "date-fns"; -import { enUS } from "date-fns/locale"; -import { tz } from "@date-fns/tz"; -import { utc } from "@date-fns/utc"; -import * as config from "../../lib/config"; import type { ComponentPropsWithoutRef } from "react"; export type RelativeTimeProps = ComponentPropsWithoutRef<"time"> & { @@ -17,17 +14,11 @@ const RelativeTime = ({ date, ...rest }: RelativeTimeProps) => { // cause a react hydration mismatch error. const hasMounted = useHasMounted(); - return ( - - ); + if (!hasMounted) { + return