Skip to content

Commit

Permalink
good enough view counter
Browse files Browse the repository at this point in the history
  • Loading branch information
FranciscoMoretti committed Jan 1, 2025
1 parent 68d78cb commit 5e363d1
Showing 12 changed files with 65 additions and 38 deletions.
4 changes: 3 additions & 1 deletion app/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use server'

import { revalidateTag, unstable_cache, unstable_noStore } from 'next/cache'
import { revalidateTag, unstable_cache } from 'next/cache'

import { db } from '@/lib/db'

@@ -23,6 +23,8 @@ export async function upsertIncreasePostViews({ slug }: { slug: string }) {
},
})
revalidateTag('post-views')

const newRes = await getAllViews()
return result
}

1 change: 1 addition & 0 deletions app/api/posts/[slug]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { upsertIncreasePostViews } from '@/app/actions'
export const dynamic = 'force-dynamic'

export async function POST(request: Request, { params }: { params: Promise<{ slug: string }> }) {
try {
25 changes: 14 additions & 11 deletions app/providers.tsx
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import { TailwindIndicator } from '@/components/tailwind-indicator'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { Suspense } from 'react'
import { NavigationEvents } from '@/components/navigation-events'
import { ReactQueryProvider } from './react-query-provider'

const PHProvider = dynamic(
() => import('@/components/posthog-provider').then((mod) => mod.PHProvider),
@@ -23,18 +24,20 @@ export function Providers({ children }: { children: React.ReactNode }) {
enableSystem
disableTransitionOnChange
>
{/* Render children immediately */}
{children}
<ReactQueryProvider>
{/* Render children immediately */}
{children}

{/* Defer analytics initialization */}
<Suspense fallback={null}>
{process.env.NEXT_PUBLIC_POSTHOG_KEY && (
<PHProvider>
<PostHogPostHogPageView />
</PHProvider>
)}
<NavigationEvents />
</Suspense>
{/* Defer analytics initialization */}
<Suspense fallback={null}>
{process.env.NEXT_PUBLIC_POSTHOG_KEY && (
<PHProvider>
<PostHogPostHogPageView />
</PHProvider>
)}
<NavigationEvents />
</Suspense>
</ReactQueryProvider>
<TailwindIndicator />
</NextThemesProvider>
)
16 changes: 16 additions & 0 deletions app/react-query-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client'

import React from 'react'
import { QueryClientProvider, QueryClient } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

export function ReactQueryProvider({ children }: React.PropsWithChildren) {
const [client] = React.useState(new QueryClient())

return (
<QueryClientProvider client={client}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
1 change: 1 addition & 0 deletions app/seo.tsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ interface PageSEOProps {

export function genPageMetadata({ title, description, image, ...rest }: PageSEOProps): Metadata {
return {
metadataBase: new URL(siteMetadata.siteUrl),
title,
description: description || siteMetadata.description,
openGraph: {
Binary file modified bun.lockb
Binary file not shown.
11 changes: 8 additions & 3 deletions components/navigation-events.tsx
Original file line number Diff line number Diff line change
@@ -2,26 +2,31 @@

import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { useQueryClient } from '@tanstack/react-query'

export function NavigationEvents() {
const pathname = usePathname()
const queryClient = useQueryClient()

useEffect(() => {
// TODO: create simpler logic
const slug = pathname?.split('/blog/').slice(1).join('/').split('#')[0]

if (slug && process.env.NODE_ENV === 'production') {
if (slug) {
fetch('/api/posts/' + slug, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
.then((response) => response)
.then((response) => {
queryClient.invalidateQueries({ queryKey: ['postViews', slug] })
return response
})
.catch((error) => console.error(error))
}
}, [pathname])
}, [pathname, queryClient])

return null
}
29 changes: 11 additions & 18 deletions components/post-views.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
'use client'

import { useEffect, useState } from 'react'

import { getPostViews } from '@/app/actions'
import { useQuery } from '@tanstack/react-query'

export function PostViews({ slug, prev }: { slug: string; prev: number }) {
return (
<span>
<span title="views">
<ViewCount slug={slug} prev={prev} /> views
</span>
)
}

function ViewCount({ slug, prev }: { slug: string; prev: number }) {
const [views, setViews] = useState(prev)

useEffect(() => {
// Fetch updated view count
async function fetchViews() {
// Wait 1 second to view to be added to db
await new Promise((resolve) => setTimeout(resolve, 1000))
getPostViews({ slug }).then((count) => {
if (count != null) setViews(count)
})
}
fetchViews()
}, [slug])

return <>{views ?? '-'}</>
const { data } = useQuery({
queryKey: ['postViews', slug],
queryFn: async () => {
// Add artificial delay for smoother UI transition when switching between posts
await new Promise((resolve) => setTimeout(resolve, 500))
return getPostViews({ slug })
},
})
return <>{data != null ? data : prev}</>
}
5 changes: 3 additions & 2 deletions layouts/ListLayout.tsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import type { Blog } from 'contentlayer/generated'
import Link from '@/components/Link'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import { PostViews } from '@/components/post-views'

interface PaginationProps {
totalPages: number
@@ -113,7 +114,7 @@ export default function ListLayout({
<ul>
{!filteredBlogPosts.length && 'No posts found.'}
{displayPosts.map((post) => {
const { path, date, title, summary, tags, viewCount } = post
const { path, date, title, summary, tags, viewCount, slug } = post
return (
<li key={path} className="py-4">
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0">
@@ -128,7 +129,7 @@ export default function ListLayout({
<div className="">
<dt className="sr-only ">View count</dt>
<dd className="flex flex-row items-center gap-1 text-sm font-medium text-muted-foreground">
{viewCount} views
<PostViews slug={slug} prev={viewCount} />
</dd>
</div>
)}
5 changes: 3 additions & 2 deletions layouts/ListLayoutWithTags.tsx
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import Link from '@/components/Link'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import tagData from 'app/tag-data.json'
import { PostViews } from '@/components/post-views'

interface PaginationProps {
totalPages: number
@@ -121,7 +122,7 @@ export default function ListLayoutWithTags({
<div>
<ul>
{displayPosts.map((post) => {
const { path, date, title, summary, tags, viewCount } = post
const { path, date, title, summary, tags, viewCount, slug } = post
return (
<li key={path} className="py-5">
<article className="flex flex-col space-y-2">
@@ -136,7 +137,7 @@ export default function ListLayoutWithTags({
<div className="">
<dt className="sr-only ">View count</dt>
<dd className="flex flex-row items-center gap-1 text-sm font-medium text-muted-foreground">
{viewCount} views
<PostViews slug={slug} prev={viewCount} />
</dd>
</div>
)}
4 changes: 3 additions & 1 deletion lib/db.ts
Original file line number Diff line number Diff line change
@@ -2,8 +2,10 @@ import { createClient } from '@libsql/client'
import { PrismaLibSQL } from '@prisma/adapter-libsql'
import { PrismaClient } from '@prisma/client'

const dbEnabled = process.env.TURSO_DATABASE_URL && process.env.TURSO_AUTH_TOKEN

function createPrismaClient() {
if (!process.env.TURSO_DATABASE_URL || !process.env.TURSO_AUTH_TOKEN) {
if (!dbEnabled) {
return null
}

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@
"@radix-ui/react-slot": "^1.1.1",
"@rehype-pretty/transformers": "^0.13.2",
"@tailwindcss/typography": "^0.5.15",
"@tanstack/react-query": "^5.62.11",
"@tanstack/react-query-devtools": "^5.62.11",
"autoprefixer": "^10.4.13",
"body-scroll-lock": "^4.0.0-beta.0",
"class-variance-authority": "^0.7.1",

0 comments on commit 5e363d1

Please sign in to comment.