mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 17:28:27 -04:00
have hit counter start at zero during suspense, then count up
This commit is contained in:
parent
bbf6e9dc66
commit
0080c4925b
@ -4,6 +4,7 @@
|
|||||||
[](https://nextjs.org/)
|
[](https://nextjs.org/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/jakejarvis/jarv.is)
|
[](https://github.com/jakejarvis/jarv.is)
|
||||||
|
[](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).
|
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).
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { connection } from "next/server";
|
import { connection } from "next/server";
|
||||||
import commaNumber from "comma-number";
|
import commaNumber from "comma-number";
|
||||||
|
import CountUp from "../../../components/CountUp";
|
||||||
import redis from "../../../lib/helpers/redis";
|
import redis from "../../../lib/helpers/redis";
|
||||||
|
|
||||||
const HitCounter = async ({ slug }: { slug: string }) => {
|
const HitCounter = async ({ slug }: { slug: string }) => {
|
||||||
@ -9,7 +10,11 @@ const HitCounter = async ({ slug }: { slug: string }) => {
|
|||||||
const hits = await redis.incr(slug);
|
const hits = await redis.incr(slug);
|
||||||
|
|
||||||
// we have data!
|
// 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) {
|
} catch (error) {
|
||||||
console.error("[hit counter] fatal error:", error);
|
console.error("[hit counter] fatal error:", error);
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.meta .metaIcon {
|
.meta .metaIcon {
|
||||||
display: inline;
|
display: inline-block;
|
||||||
width: 1.2em;
|
width: 1.2em;
|
||||||
height: 1.2em;
|
height: 1.2em;
|
||||||
vertical-align: -0.2em;
|
vertical-align: -0.25em;
|
||||||
margin-right: 0.6em;
|
margin-right: 0.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,11 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EyeIcon size="1.2em" className={styles.metaIcon} />
|
<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}`} />
|
<HitCounter slug={`notes/${frontmatter!.slug}`} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
5
components/CountUp/index.ts
Normal file
5
components/CountUp/index.ts
Normal 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";
|
@ -1,6 +1,7 @@
|
|||||||
import { Redis } from "@upstash/redis";
|
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();
|
const redis = Redis.fromEnv();
|
||||||
|
|
||||||
export default redis;
|
export default redis;
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
"polished": "^4.3.1",
|
"polished": "^4.3.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
|
"react-countup": "^6.5.3",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-error-boundary": "^5.0.0",
|
"react-error-boundary": "^5.0.0",
|
||||||
"react-innertext": "^1.1.5",
|
"react-innertext": "^1.1.5",
|
||||||
|
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@ -89,6 +89,9 @@ importers:
|
|||||||
react:
|
react:
|
||||||
specifier: 19.0.0
|
specifier: 19.0.0
|
||||||
version: 19.0.0
|
version: 19.0.0
|
||||||
|
react-countup:
|
||||||
|
specifier: ^6.5.3
|
||||||
|
version: 6.5.3(react@19.0.0)
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: 19.0.0
|
specifier: 19.0.0
|
||||||
version: 19.0.0(react@19.0.0)
|
version: 19.0.0(react@19.0.0)
|
||||||
@ -1320,6 +1323,9 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
countup.js@2.8.0:
|
||||||
|
resolution: {integrity: sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ==}
|
||||||
|
|
||||||
cross-env@7.0.3:
|
cross-env@7.0.3:
|
||||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||||
@ -2910,6 +2916,11 @@ packages:
|
|||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
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:
|
react-dom@19.0.0:
|
||||||
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
|
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -4835,6 +4846,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.8.2
|
typescript: 5.8.2
|
||||||
|
|
||||||
|
countup.js@2.8.0: {}
|
||||||
|
|
||||||
cross-env@7.0.3:
|
cross-env@7.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
@ -6946,6 +6959,11 @@ snapshots:
|
|||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
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):
|
react-dom@19.0.0(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user