1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2026-06-05 19:15:30 -04:00

Enhance notes page with comment counts and display. Update data fetching to include comment counts alongside views, and integrate comment count badges in both the notes listing and individual post pages.

This commit is contained in:
2025-09-07 16:26:45 -04:00
parent 5917811229
commit 2fa9b73f8d
8 changed files with 414 additions and 311 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
import { NextResponse } from "next/server";
import { unstable_cache as cache } from "next/cache";
import { getViews as _getViews } from "@/lib/views";
import { getViewCounts as _getViewCounts } from "@/lib/views";
const getViews = cache(_getViews, undefined, {
const getViewCounts = cache(_getViewCounts, undefined, {
revalidate: 300, // 5 minutes
tags: ["hits"],
});
@@ -19,7 +19,7 @@ export const GET = async (): Promise<
}>
> => {
// note: while hits have been renamed to views in most places, this API shouldn't change due to it being snapshotted
const views = await getViews();
const views = await getViewCounts();
const total = {
hits: Object.values(views).reduce((acc, curr) => acc + curr, 0),
+12 -1
View File
@@ -2,7 +2,7 @@ import { env } from "@/lib/env";
import { Suspense } from "react";
import { JsonLd } from "react-schemaorg";
import { formatDate, formatDateISO } from "@/lib/date";
import { CalendarDaysIcon, TagIcon, SquarePenIcon, EyeIcon } from "lucide-react";
import { CalendarDaysIcon, TagIcon, SquarePenIcon, EyeIcon, MessagesSquareIcon } from "lucide-react";
import Link from "@/components/link";
import ViewCounter from "@/components/view-counter";
import Comments from "@/components/comments/comments";
@@ -12,6 +12,7 @@ import { createMetadata } from "@/lib/metadata";
import siteConfig from "@/lib/config/site";
import authorConfig from "@/lib/config/author";
import { size as ogImageSize } from "./opengraph-image";
import { getCommentCounts } from "@/lib/server/comments";
import type { Metadata } from "next";
import type { BlogPosting } from "schema-dts";
@@ -54,6 +55,7 @@ export const generateMetadata = async ({ params }: { params: Promise<{ slug: str
const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
const { slug } = await params;
const frontmatter = await getFrontMatter(slug);
const commentCount = await getCommentCounts(`${POSTS_DIR}/${slug}`);
const { default: MDXContent } = await import(`../../../${POSTS_DIR}/${slug}/index.mdx`);
@@ -120,6 +122,15 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
<span>Improve This Post</span>
</Link>
<Link
href={`/${POSTS_DIR}/${frontmatter!.slug}#comments`}
title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(commentCount || 0)} ${commentCount === 1 ? "comment" : "comments"}`}
className="text-foreground/70 flex flex-nowrap items-center gap-x-2 whitespace-nowrap hover:no-underline"
>
<MessagesSquareIcon className="inline size-4 shrink-0" />
<span>{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(commentCount || 0)}</span>
</Link>
<div className="flex min-w-14 flex-nowrap items-center gap-x-2 whitespace-nowrap">
<EyeIcon className="inline size-4 shrink-0" />
<Suspense
+22 -5
View File
@@ -1,11 +1,12 @@
import { env } from "@/lib/env";
import { EyeIcon } from "lucide-react";
import { EyeIcon, MessagesSquareIcon } from "lucide-react";
import Link from "@/components/link";
import { getFrontMatter, POSTS_DIR, type FrontMatter } from "@/lib/posts";
import { createMetadata } from "@/lib/metadata";
import { formatDate, formatDateISO } from "@/lib/date";
import authorConfig from "@/lib/config/author";
import { getViews } from "@/lib/views";
import { getViewCounts } from "@/lib/views";
import { getCommentCounts } from "@/lib/server/comments";
export const revalidate = 300; // 5 minutes
@@ -17,10 +18,10 @@ export const metadata = createMetadata({
const Page = async () => {
// parse the year of each post and group them together
const [posts, views] = await Promise.all([getFrontMatter(), getViews()]);
const [posts, views, comments] = await Promise.all([getFrontMatter(), getViewCounts(), getCommentCounts()]);
const postsByYear: {
[year: string]: (FrontMatter & { views: number })[];
[year: string]: (FrontMatter & { views: number; comments: number })[];
} = {};
posts.forEach((post) => {
@@ -28,6 +29,7 @@ const Page = async () => {
(postsByYear[year] || (postsByYear[year] = [])).push({
...post,
views: views[`${POSTS_DIR}/${post.slug}`] || 0,
comments: comments[`${POSTS_DIR}/${post.slug}`] || 0,
});
});
@@ -40,7 +42,7 @@ const Page = async () => {
{year}
</h2>
<ul className="space-y-4">
{posts.map(({ slug, date, title, htmlTitle, views }) => (
{posts.map(({ slug, date, title, htmlTitle, views, comments }) => (
<li className="flex text-base leading-relaxed" key={slug}>
<span className="text-muted-foreground w-18 shrink-0 md:w-22">
<time dateTime={formatDateISO(date)} title={formatDate(date, "MMM d, y, h:mm a O")}>
@@ -62,6 +64,21 @@ const Page = async () => {
</span>
</span>
)}
{comments > 0 && (
<Link
href={`/${POSTS_DIR}/${slug}#comments`}
title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(comments)} ${comments === 1 ? "comment" : "comments"}`}
className="inline-flex hover:no-underline"
>
<span className="bg-muted text-foreground/65 inline-flex h-5 flex-nowrap items-center gap-1 rounded-md px-1.5 align-text-top text-xs font-semibold text-nowrap shadow select-none">
<MessagesSquareIcon className="inline-block size-3 shrink-0" />
<span className="inline-block leading-none">
{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(comments)}
</span>
</span>
</Link>
)}
</div>
</li>
))}