From 0080c4925bc547f424573d6a19788b7931038afc Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Thu, 27 Mar 2025 10:08:18 -0400 Subject: [PATCH] have hit counter start at zero during suspense, then count up --- README.md | 1 + app/notes/[slug]/counter.tsx | 7 ++++++- app/notes/[slug]/page.module.css | 4 ++-- app/notes/[slug]/page.tsx | 6 +++++- components/CountUp/index.ts | 5 +++++ lib/helpers/redis.ts | 3 ++- package.json | 1 + pnpm-lock.yaml | 18 ++++++++++++++++++ 8 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 components/CountUp/index.ts diff --git a/README.md b/README.md index 2c973a89..255d8aed 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Next.js version](https://img.shields.io/github/package-json/dependency-version/jakejarvis/jarv.is/next/main?color=ff4088&label=next.js&logo=nextdotjs&logoColor=white)](https://nextjs.org/) [![Licensed under CC-BY-4.0](https://img.shields.io/badge/license-CC--BY--4.0-fb7828?logo=creative-commons&logoColor=white)](LICENSE) [![GitHub repo size](https://img.shields.io/github/repo-size/jakejarvis/jarv.is?color=009cdf&label=repo%20size&logo=git&logoColor=white)](https://github.com/jakejarvis/jarv.is) +[![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fjarv.is%2Fapi%2Fhits&query=%24.total.hits&logo=googleanalytics&logoColor=white&label=hits&color=salmon&cacheSeconds=1800)](https://jarv.is/api/hits) My humble abode on the World Wide Web, created and deployed using [Next.js](https://nextjs.org/), [Vercel](https://vercel.com/), [Upstash Redis](https://upstash.com/), [Giscus](https://giscus.app/), [Umami](https://umami.is/), [and more](https://jarv.is/humans.txt). diff --git a/app/notes/[slug]/counter.tsx b/app/notes/[slug]/counter.tsx index ca2a1fae..7f18e2ad 100644 --- a/app/notes/[slug]/counter.tsx +++ b/app/notes/[slug]/counter.tsx @@ -1,5 +1,6 @@ import { connection } from "next/server"; import commaNumber from "comma-number"; +import CountUp from "../../../components/CountUp"; import redis from "../../../lib/helpers/redis"; const HitCounter = async ({ slug }: { slug: string }) => { @@ -9,7 +10,11 @@ const HitCounter = async ({ slug }: { slug: string }) => { const hits = await redis.incr(slug); // we have data! - return {commaNumber(hits)}; + return ( + + + + ); } catch (error) { console.error("[hit counter] fatal error:", error); diff --git a/app/notes/[slug]/page.module.css b/app/notes/[slug]/page.module.css index 28e8bab3..da0658ab 100644 --- a/app/notes/[slug]/page.module.css +++ b/app/notes/[slug]/page.module.css @@ -17,10 +17,10 @@ } .meta .metaIcon { - display: inline; + display: inline-block; width: 1.2em; height: 1.2em; - vertical-align: -0.2em; + vertical-align: -0.25em; margin-right: 0.6em; } diff --git a/app/notes/[slug]/page.tsx b/app/notes/[slug]/page.tsx index e3660070..2dcfdbdb 100644 --- a/app/notes/[slug]/page.tsx +++ b/app/notes/[slug]/page.tsx @@ -128,7 +128,11 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => { }} > - }> + 0} + > diff --git a/components/CountUp/index.ts b/components/CountUp/index.ts new file mode 100644 index 00000000..ea09e496 --- /dev/null +++ b/components/CountUp/index.ts @@ -0,0 +1,5 @@ +"use client"; + +// marking the library as a proper client component so that react doesn't complain about hydration whenever we use it in +// a server component. +export { default } from "react-countup"; diff --git a/lib/helpers/redis.ts b/lib/helpers/redis.ts index a98d49dd..e6e2e6e2 100644 --- a/lib/helpers/redis.ts +++ b/lib/helpers/redis.ts @@ -1,6 +1,7 @@ import { Redis } from "@upstash/redis"; -// Initialize Redis +// pulls credentials (prefixed with 'KV_REST_API_') set automatically by Vercel marketplace integration: +// https://github.com/upstash/redis-js/blob/091e0a0949593d74b905f41f7cb409ada16f936f/platforms/nodejs.ts#L184 const redis = Redis.fromEnv(); export default redis; diff --git a/package.json b/package.json index d8d8f7f1..a18ca947 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "polished": "^4.3.1", "prop-types": "^15.8.1", "react": "19.0.0", + "react-countup": "^6.5.3", "react-dom": "19.0.0", "react-error-boundary": "^5.0.0", "react-innertext": "^1.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12daafde..11de5342 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: react: specifier: 19.0.0 version: 19.0.0 + react-countup: + specifier: ^6.5.3 + version: 6.5.3(react@19.0.0) react-dom: specifier: 19.0.0 version: 19.0.0(react@19.0.0) @@ -1320,6 +1323,9 @@ packages: typescript: optional: true + countup.js@2.8.0: + resolution: {integrity: sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ==} + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -2910,6 +2916,11 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-countup@6.5.3: + resolution: {integrity: sha512-udnqVQitxC7QWADSPDOxVWULkLvKUWrDapn5i53HE4DPRVgs+Y5rr4bo25qEl8jSh+0l2cToJgGMx+clxPM3+w==} + peerDependencies: + react: '>= 16.3.0' + react-dom@19.0.0: resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: @@ -4835,6 +4846,8 @@ snapshots: optionalDependencies: typescript: 5.8.2 + countup.js@2.8.0: {} + cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -6946,6 +6959,11 @@ snapshots: queue-microtask@1.2.3: {} + react-countup@6.5.3(react@19.0.0): + dependencies: + countup.js: 2.8.0 + react: 19.0.0 + react-dom@19.0.0(react@19.0.0): dependencies: react: 19.0.0