From 28bbaa52f1790daec06cb5e09e9fbf1ee7d05218 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Sun, 19 Apr 2026 18:47:11 +0100 Subject: [PATCH] fix: better like count --- .../migration.sql | 7 ++ prisma/schema.prisma | 6 +- src/app/api/mii/[id]/like/route.ts | 92 ++++++++++--------- src/app/edit/[id]/page.tsx | 2 +- src/app/mii/[id]/page.tsx | 8 +- src/app/report/mii/[id]/page.tsx | 2 +- src/components/mii/author-buttons.tsx | 2 +- src/components/mii/list/index.tsx | 7 +- src/components/mii/list/mii-grid.tsx | 6 +- 9 files changed, 70 insertions(+), 62 deletions(-) create mode 100644 prisma/migrations/20260419170916_use_like_count/migration.sql diff --git a/prisma/migrations/20260419170916_use_like_count/migration.sql b/prisma/migrations/20260419170916_use_like_count/migration.sql new file mode 100644 index 0000000..f5128be --- /dev/null +++ b/prisma/migrations/20260419170916_use_like_count/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +ALTER TABLE "miis" ADD COLUMN "likeCount" INTEGER NOT NULL DEFAULT 0; + +-- CreateIndex +CREATE INDEX "miis_likeCount_idx" ON "miis"("likeCount" DESC); + +UPDATE miis SET like_count = (SELECT COUNT(*) FROM likes WHERE likes."miiId" = miis.id); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c3cc1cc..dca9d42 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -90,14 +90,16 @@ model Mii { createdAt DateTime @default(now()) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - likedBy Like[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + likeCount Int @default(0) punishmentId Int? punishments MiiPunishment[] + likedBy Like[] @@index([tags], type: Gin) @@index([createdAt]) + @@index([likeCount(sort: Desc)]) @@index([quarantined, createdAt(sort: Desc)]) @@index([platform, createdAt(sort: Desc)]) @@index([userId, createdAt(sort: Desc)]) diff --git a/src/app/api/mii/[id]/like/route.ts b/src/app/api/mii/[id]/like/route.ts index 5680e12..5a762b6 100644 --- a/src/app/api/mii/[id]/like/route.ts +++ b/src/app/api/mii/[id]/like/route.ts @@ -5,55 +5,57 @@ import { prisma } from "@/lib/prisma"; import { idSchema } from "@/lib/schemas"; import { RateLimit } from "@/lib/rate-limit"; -export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - // const session = await auth(); - // if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); +export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { + const session = await auth(); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); - // const rateLimit = new RateLimit(request, 100, "/api/mii/like"); - // const check = await rateLimit.handle(); - // if (check) return check; + const rateLimit = new RateLimit(request, 100, "/api/mii/like"); + const check = await rateLimit.handle(); + if (check) return check; - // const { id: slugId } = await params; - // const parsed = idSchema.safeParse(slugId); - // if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400); - // const miiId = parsed.data; + const { id: slugId } = await params; + const parsed = idSchema.safeParse(slugId); + if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400); + const miiId = parsed.data; - // const result = await prisma.$transaction(async (tx) => { - // const existingLike = await tx.like.findUnique({ - // where: { - // userId_miiId: { - // userId: Number(session.user?.id), - // miiId, - // }, - // }, - // }); + const result = await prisma.$transaction(async (tx) => { + const existingLike = await tx.like.findUnique({ + where: { + userId_miiId: { + userId: Number(session.user?.id), + miiId, + }, + }, + }); - // if (existingLike) { - // // Remove the like if it exists - // await tx.like.delete({ - // where: { - // userId_miiId: { - // userId: Number(session.user?.id), - // miiId, - // }, - // }, - // }); - // } else { - // // Add a like if it doesn't exist - // await tx.like.create({ - // data: { - // userId: Number(session.user?.id), - // miiId, - // }, - // }); - // } + if (existingLike) { + await tx.like.delete({ + where: { + userId_miiId: { + userId: Number(session.user?.id), + miiId, + }, + }, + }); + await tx.mii.update({ + where: { id: miiId }, + data: { likeCount: { decrement: 1 } }, + }); + } else { + await tx.like.create({ + data: { + userId: Number(session.user?.id), + miiId, + }, + }); + await tx.mii.update({ + where: { id: miiId }, + data: { likeCount: { increment: 1 } }, + }); + } - // const likeCount = await tx.like.count({ - // where: { miiId }, - // }); + return { liked: !existingLike }; + }); - // return { liked: !existingLike, count: likeCount }; - // }); - - return NextResponse.json({ success: false }); + return rateLimit.sendResponse({ success: true, liked: result.liked }); } diff --git a/src/app/edit/[id]/page.tsx b/src/app/edit/[id]/page.tsx index 3008146..c52b2cc 100644 --- a/src/app/edit/[id]/page.tsx +++ b/src/app/edit/[id]/page.tsx @@ -46,5 +46,5 @@ export default async function MiiPage({ params }: Props) { // Check ownership if (!mii || (Number(session?.user?.id) !== mii.userId && Number(session?.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID))) redirect("/404"); - return ; + return ; } diff --git a/src/app/mii/[id]/page.tsx b/src/app/mii/[id]/page.tsx index ec31c1e..c8d7511 100644 --- a/src/app/mii/[id]/page.tsx +++ b/src/app/mii/[id]/page.tsx @@ -51,13 +51,13 @@ export async function generateMetadata({ params }: Props): Promise { return { metadataBase: new URL(process.env.NEXT_PUBLIC_BASE_URL!), title: `${mii.name} - TomodachiShare`, - description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii._count.likedBy} likes.`, + description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii.likeCount} likes.`, keywords: ["mii", "tomodachi life", "nintendo", "tomodachishare", "tomodachi-share", "mii creator", "mii collection", ...mii.tags], creator: name, openGraph: { type: "article", title: `${mii.name} - TomodachiShare`, - description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii._count.likedBy} likes.`, + description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii.likeCount} likes.`, images: [ { url: metadataImageUrl, @@ -70,7 +70,7 @@ export async function generateMetadata({ params }: Props): Promise { twitter: { card: "summary_large_image", title: `${mii.name} - TomodachiShare`, - description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii._count.likedBy} likes.`, + description: `Check out '${mii.name}', a ${mii.platform === MiiPlatform.SWITCH ? "Switch Living the Dream" : "3DS"} Tomodachi Life Mii created by ${mii.name} on TomodachiShare with ${mii.likeCount} likes.`, images: [ { url: metadataImageUrl, @@ -306,7 +306,7 @@ export default async function MiiPage({ params }: Props) { {/* Submission name */}

{mii.name}

{/* Like button */} - + {/* Tags */}
diff --git a/src/app/report/mii/[id]/page.tsx b/src/app/report/mii/[id]/page.tsx index da40d1a..86b8211 100644 --- a/src/app/report/mii/[id]/page.tsx +++ b/src/app/report/mii/[id]/page.tsx @@ -41,7 +41,7 @@ export default async function ReportMiiPage({ params }: Props) { return (
- +
); } diff --git a/src/components/mii/author-buttons.tsx b/src/components/mii/author-buttons.tsx index 1a9414b..9555c7f 100644 --- a/src/components/mii/author-buttons.tsx +++ b/src/components/mii/author-buttons.tsx @@ -31,7 +31,7 @@ export default function AuthorButtons({ mii }: Props) { Edit - + ); } diff --git a/src/components/mii/list/index.tsx b/src/components/mii/list/index.tsx index 33042eb..0043cab 100644 --- a/src/components/mii/list/index.tsx +++ b/src/components/mii/list/index.tsx @@ -89,6 +89,7 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop allowedCopying: true, quarantined: true, in_queue: true, + likeCount: true, // Mii liked check ...(session?.user?.id && { likedBy: { @@ -96,10 +97,6 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop select: { userId: true }, }, }), - // Like count - _count: { - select: { likedBy: true }, - }, }; const skip = (page - 1) * limit; @@ -111,7 +108,7 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop let orderBy: Prisma.MiiOrderByWithRelationInput[]; if (sort === "likes") { - orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }]; + orderBy = [{ likeCount: "desc" }, { name: "asc" }]; } else if (sort === "oldest") { orderBy = [{ createdAt: "asc" }, { name: "asc" }]; } else { diff --git a/src/components/mii/list/mii-grid.tsx b/src/components/mii/list/mii-grid.tsx index 4dc6f54..0a07e13 100644 --- a/src/components/mii/list/mii-grid.tsx +++ b/src/components/mii/list/mii-grid.tsx @@ -74,7 +74,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) {
- + {!userId && ( @@ -87,7 +87,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) { - +
)} @@ -105,7 +105,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) {
- +