1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 03:05:24 -04:00

have hit counter start at zero during suspense, then count up

This commit is contained in:
Jake Jarvis 2025-03-27 10:08:18 -04:00
parent bbf6e9dc66
commit 0080c4925b
Signed by: jake
SSH Key Fingerprint: SHA256:nCkvAjYA6XaSPUqc4TfbBQTpzr8Xj7ritg/sGInCdkc
8 changed files with 40 additions and 5 deletions

View File

@ -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).

View File

@ -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 <span title={`${commaNumber(hits)} ${hits === 1 ? "view" : "views"}`}>{commaNumber(hits)}</span>;
return (
<span title={`${commaNumber(hits)} ${hits === 1 ? "view" : "views"}`}>
<CountUp start={0} end={hits} delay={0} duration={2.5} />
</span>
);
} catch (error) {
console.error("[hit counter] fatal error:", error);

View File

@ -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;
}

View File

@ -128,7 +128,11 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
}}
>
<EyeIcon size="1.2em" className={styles.metaIcon} />
<Suspense fallback={<Loading boxes={3} width={20} />}>
<Suspense
// when this loads, the component will count up from zero to the actual number of hits, so we can simply
// show a zero here as a "loading indicator"
fallback={<span>0</span>}
>
<HitCounter slug={`notes/${frontmatter!.slug}`} />
</Suspense>
</div>

View File

@ -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";

View File

@ -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;

View File

@ -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",

18
pnpm-lock.yaml generated
View File

@ -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