From f00c998c30910f3d4781a5e0a8c1228712ed7a9e Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Mon, 14 Apr 2025 18:10:51 +0100 Subject: [PATCH] refactor: add slug to mii like route --- src/app/api/mii/{ => [id]}/like/route.ts | 17 +++++++-------- src/app/components/like-button.tsx | 27 +++++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) rename src/app/api/mii/{ => [id]}/like/route.ts (75%) diff --git a/src/app/api/mii/like/route.ts b/src/app/api/mii/[id]/like/route.ts similarity index 75% rename from src/app/api/mii/like/route.ts rename to src/app/api/mii/[id]/like/route.ts index 28ffa5c..bc9f38e 100644 --- a/src/app/api/mii/like/route.ts +++ b/src/app/api/mii/[id]/like/route.ts @@ -4,21 +4,20 @@ import { z } from "zod"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; -const likeSchema = z.object({ - miiId: z.coerce.number().int({ message: "Mii ID must be an integer" }).positive({ message: "Mii ID must be valid" }), -}); - -export async function PATCH(request: NextRequest) { - // todo: rate limit +const slugSchema = z.coerce + .number({ message: "Mii ID must be a number" }) + .int({ message: "Mii ID must be an integer" }) + .positive({ message: "Mii ID must be valid" }); +export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { const session = await auth(); if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); - const body = await request.json(); - const parsed = likeSchema.safeParse(body); + const { id: slugId } = await params; + const parsed = slugSchema.safeParse(slugId); if (!parsed.success) return NextResponse.json({ error: parsed.error.errors[0].message }, { status: 400 }); - const { miiId } = parsed.data; + const miiId = parsed.data; const result = await prisma.$transaction(async (tx) => { const existingLike = await tx.like.findUnique({ diff --git a/src/app/components/like-button.tsx b/src/app/components/like-button.tsx index 3028b62..5e9cef5 100644 --- a/src/app/components/like-button.tsx +++ b/src/app/components/like-button.tsx @@ -6,31 +6,38 @@ import { Icon } from "@iconify/react"; interface Props { likes: number; - miiId: number | undefined; + miiId?: number | undefined; isLiked: boolean; - isLoggedIn: boolean; + isLoggedIn?: boolean; + disabled?: boolean; big?: boolean; } -export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, big }: Props) { +export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled, big }: Props) { const [isLikedState, setIsLikedState] = useState(isLiked); const [likesState, setLikesState] = useState(likes); const onClick = async () => { + if (disabled) return; if (!isLoggedIn) redirect("/login"); - setIsLikedState((prev) => !prev); - setLikesState((prev) => (isLiked ? prev - 1 : prev + 1)); + setIsLikedState(!isLikedState); + setLikesState(isLikedState ? likesState - 1 : likesState + 1); - const response = await fetch("/api/mii/like", { method: "PATCH", body: JSON.stringify({ miiId }) }); - const { liked, count } = await response.json(); + const response = await fetch(`/api/mii/${miiId}/like`, { method: "PATCH" }); - setIsLikedState(liked); - setLikesState(count); + if (response.ok) { + const { liked, count } = await response.json(); + setIsLikedState(liked); + setLikesState(count); + } else { + setIsLikedState(isLikedState); + setLikesState(likesState); + } }; return ( -