Skip to content

Commit

Permalink
Add views to blog posts
Browse files Browse the repository at this point in the history
  • Loading branch information
Kawtar Choubari committed Nov 1, 2023
1 parent 3a682ba commit 897876d
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 59 deletions.
2 changes: 1 addition & 1 deletion app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function About() {
<span className="text-2xl leading-3 text-accent">.</span>
</h2>
<p className="mb-4">
Hey! I'm <span className="font-bold">Kawtar CHOUBARI</span>, a Junior
Hey! I'm <span className="font-bold">Kawtar CHOUBARI</span>, a
Software Engineer and Content Creator from Morocco, currently living
in Paris, France.
</p>
Expand Down
34 changes: 34 additions & 0 deletions app/api/views/[slug]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextResponse } from "next/server";
import { SupabaseAdmin } from "@/lib/supabase";

export async function POST(
req: Request,
{ params }: { params: { slug: string } }
) {
try {
await SupabaseAdmin.rpc("increment_post_view", { post_slug: params.slug });

return NextResponse.json({
message: `Successfully incremented post: ${params.slug}`,
});
} catch (error) {
return NextResponse.json({ error });
}
}

export async function GET(
req: Request,
{ params }: { params: { slug: string } }
) {
const { data } = await SupabaseAdmin.from("posts")
.select("view_count")
.filter("slug", "eq", params.slug);

if (data)
return NextResponse.json({
total: data[0]?.view_count || null,
});
return NextResponse.json({
message: "Unsupported Request",
});
}
24 changes: 15 additions & 9 deletions app/blog/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { FaCalendar, FaChevronLeft } from "react-icons/fa";
import Link from "next/link";
import Image from "next/image";
import { Mdx } from "@/components/mdx/mdx-components";
import { absoluteUrl } from "@/lib/utils";
import { absoluteUrl, incrementPostViews } from "@/lib/utils";
import PostViews from "@/components/mdx/post-views";

interface PostPageProps {
params: {
Expand Down Expand Up @@ -78,6 +79,7 @@ export default async function PostPage({ params }: PostPageProps) {
if (!post) {
notFound();
}
await incrementPostViews(post.slugAsParams);

return (
<article className="container relative mx-auto max-w-3xl py-6 px-5 lg:py-10">
Expand All @@ -92,14 +94,18 @@ export default async function PostPage({ params }: PostPageProps) {
<h1 className="mb-4 inline-block font-heading font-serif font-bold text-4xl leading-tight lg:text-5xl">
{post.title}
</h1>
{post.date && (
<p className="text-sm mb-2 opacity-60 flex items-center">
<FaCalendar className="inline-block mr-2" />
<time dateTime={post.date} className="italic">
{format(parseISO(post.date), "LLLL d, yyyy")}
</time>
</p>
)}
<div className="flex gap-5">
{post.date && (
<p className="text-sm mb-2 opacity-60 flex items-center">
<FaCalendar className="inline-block mr-2" />
<time dateTime={post.date} className="italic">
{format(parseISO(post.date), "LLLL d, yyyy")}
</time>
</p>
)}
{/* @ts-expect-error Async Server Component */}
<PostViews slug={post.slugAsParams} />
</div>
</div>
{post.image && (
<Image
Expand Down
44 changes: 2 additions & 42 deletions app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,11 @@
import Link from "next/link";
import Image from "next/image";
import { compareDesc, format, parseISO } from "date-fns";
import { compareDesc } from "date-fns";
import { allPosts, Post } from "contentlayer/generated";
import { FaCalendar } from "react-icons/fa";
import PostCard from "@/components/post-card";

export const metadata = {
title: "Blog",
};

function PostCard(post: Post) {
return (
<article
key={post._id}
className="mb-8 shadow-lg rounded-lg dark:bg-dark bg-lighter transform transition-all duration-300 ease-in-out hover:shadow-md hover:shadow-accent"
>
<Link href={post.slug}>
{post.image && (
<Image
src={post.image}
placeholder="blur"
blurDataURL="/blog/placeholder.jpg"
alt={post.title}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 75vw, 50vw"
width={804}
height={452}
className="rounded-md border bg-muted transition-colors"
/>
)}
<div className="px-5 pt-2 pb-5">
<h2 className="text-2xl font-serif font-bold text-accent">
{post.title}
</h2>
<p className="text-sm mb-2 opacity-60 flex items-center">
<FaCalendar className="inline-block mr-2" />
<time dateTime={post.date} className="italic">
{format(parseISO(post.date), "LLLL d, yyyy")}
</time>
</p>
{post.description && (
<p className="text-muted-foreground">{post.description}</p>
)}
</div>
</Link>
</article>
);
}

export default function Blog() {
const posts = allPosts
.filter((post) => post.published)
Expand Down
22 changes: 22 additions & 0 deletions components/mdx/post-views.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { countFormatter } from "@/lib/utils";
import { FaEye } from "react-icons/fa";

interface PostViewsProps {
slug: string;
}

export default async function PostViews({ slug }: PostViewsProps) {
const response = await fetch(
`${process.env.NEXT_PUBLIC_APP_URL}/api/views/${slug}`
);
const data = await response.json();

return (
<div className="text-sm mb-2 opacity-60 flex items-center">
<FaEye className="inline-block mr-2 text-base" />
<p className="italic">
{data?.total && `${countFormatter(data.total)} views`}
</p>
</div>
);
}
51 changes: 51 additions & 0 deletions components/post-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Link from "next/link";
import Image from "next/image";
import { format, parseISO } from "date-fns";
import { Post } from "contentlayer/generated";
import { FaCalendar } from "react-icons/fa";
import PostViews from "./mdx/post-views";

export default function PostCard(post: Post) {
return (
<article
key={post._id}
className="mb-8 shadow-lg rounded-lg dark:bg-dark bg-lighter transform transition-all duration-300 ease-in-out hover:shadow-md hover:shadow-accent"
>
<Link href={post.slug}>
{post.image && (
<Image
src={post.image}
placeholder="blur"
blurDataURL="/blog/placeholder.jpg"
alt={post.title}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 75vw, 50vw"
width={804}
height={452}
className="rounded-md border bg-muted transition-colors"
/>
)}
<div className="px-5 pt-2 pb-5">
<h2 className="text-2xl font-serif font-bold text-accent py-2">
{post.title}
</h2>
<div className="flex gap-5">
{post.date && (
<p className="text-sm mb-2 opacity-60 flex items-center">
<FaCalendar className="inline-block mr-2" />
<time dateTime={post.date} className="italic">
{format(parseISO(post.date), "LLLL d, yyyy")}
</time>
</p>
)}
{/* @ts-expect-error Async Server Component */}
<PostViews slug={post.slugAsParams} />
</div>

{post.description && (
<p className="text-muted-foreground">{post.description}</p>
)}
</div>
</Link>
</article>
);
}
7 changes: 1 addition & 6 deletions components/talk-card.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import Image from "next/image";
import {
FaCalendar,
FaPlay,
FaFilePowerpoint,
FaLaptopCode,
} from "react-icons/fa";
import { FaCalendar } from "react-icons/fa";
import { Talk } from "@/types";

type TalkCardProps = {
Expand Down
2 changes: 1 addition & 1 deletion content/blog/welcome-to-my-blog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Hello, World! Welcome to [Choubari.com](/), my personal website where I will sha

## Who Am I?

Hey! I'm Kawtar CHOUBARI, a Junior Software Engineer and Content Creator from Morocco 🇲🇦, currently living in Paris, France 🇫🇷.
Hey! I'm Kawtar CHOUBARI, a Software Engineer and Content Creator from Morocco 🇲🇦, currently living in Paris, France 🇫🇷.

I mainly work on Front-End development with JavaScript / TypeScript and frameworks like ReactJS, Next.js, and also React Native.

Expand Down
8 changes: 8 additions & 0 deletions lib/supabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createClient } from "@supabase/supabase-js";

const supabaseUrl: string = process.env.SUPABASE_URL || "";
const supabaseServerKey: string = process.env.SUPABASE_SERVICE_KEY || "";

const SupabaseAdmin = createClient(supabaseUrl, supabaseServerKey);

export { SupabaseAdmin };
7 changes: 7 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,10 @@ export const validateCaptcha = (response_key) => {
});
});
};

export async function incrementPostViews(slug: string) {
await fetch(`${process.env.NEXT_PUBLIC_APP_URL}/api/views/${slug}`, {
method: "POST",
next: { revalidate: 10 },
});
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@headlessui/react": "^1.7.15",
"@react-spring/web": "^9.7.3",
"@supabase/supabase-js": "^2.38.4",
"@tailwindcss/typography": "^0.5.9",
"@types/grecaptcha": "^3.0.4",
"@types/node": "20.2.3",
Expand Down
Loading

0 comments on commit 897876d

Please sign in to comment.