mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 13:17:45 +00:00
Compare commits
No commits in common. "e1885fd8fed2d963729d20f5a42ea71fcfd944b6" and "79b19f4807795fb0de7c2ee850d43adb43ba6e89" have entirely different histories.
e1885fd8fe
...
79b19f4807
9 changed files with 62 additions and 70 deletions
|
|
@ -1,7 +0,0 @@
|
|||
-- 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 "likeCount" = (SELECT COUNT(*) FROM likes WHERE likes."miiId" = miis.id);
|
||||
|
|
@ -90,16 +90,14 @@ model Mii {
|
|||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
likeCount Int @default(0)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
likedBy Like[]
|
||||
|
||||
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)])
|
||||
|
|
|
|||
|
|
@ -5,57 +5,55 @@ import { prisma } from "@/lib/prisma";
|
|||
import { idSchema } from "@/lib/schemas";
|
||||
import { RateLimit } from "@/lib/rate-limit";
|
||||
|
||||
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
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 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) {
|
||||
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 } },
|
||||
});
|
||||
}
|
||||
// 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,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
return { liked: !existingLike };
|
||||
});
|
||||
// const likeCount = await tx.like.count({
|
||||
// where: { miiId },
|
||||
// });
|
||||
|
||||
return rateLimit.sendResponse({ success: true, liked: result.liked });
|
||||
// return { liked: !existingLike, count: likeCount };
|
||||
// });
|
||||
|
||||
return NextResponse.json({ success: false });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <EditForm mii={mii} likes={mii.likeCount} />;
|
||||
return <EditForm mii={mii} likes={mii._count.likedBy} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
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.likeCount} 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._count.likedBy} 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.likeCount} 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._count.likedBy} likes.`,
|
||||
images: [
|
||||
{
|
||||
url: metadataImageUrl,
|
||||
|
|
@ -70,7 +70,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
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.likeCount} 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._count.likedBy} likes.`,
|
||||
images: [
|
||||
{
|
||||
url: metadataImageUrl,
|
||||
|
|
@ -306,7 +306,7 @@ export default async function MiiPage({ params }: Props) {
|
|||
{/* Submission name */}
|
||||
<h1 className="text-4xl font-extrabold wrap-break-word whitespace-break-spaces text-amber-700 flex-1 min-w-0">{mii.name}</h1>
|
||||
{/* Like button */}
|
||||
<LikeButton likes={mii.likeCount ?? 0} miiId={mii.id} isLiked={false} big />
|
||||
<LikeButton likes={mii._count.likedBy ?? 0} miiId={mii.id} isLiked={false} big />
|
||||
</div>
|
||||
{/* Tags */}
|
||||
<div id="tags" className="flex flex-wrap gap-1 mt-1 *:px-2 *:py-1 *:bg-orange-300 *:rounded-full *:text-xs">
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default async function ReportMiiPage({ params }: Props) {
|
|||
|
||||
return (
|
||||
<div className="flex justify-center w-full">
|
||||
<ReportMiiForm mii={mii} likes={mii.likeCount} />
|
||||
<ReportMiiForm mii={mii} likes={mii._count.likedBy} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default function AuthorButtons({ mii }: Props) {
|
|||
<Icon icon="mdi:pencil" />
|
||||
<span>Edit</span>
|
||||
</Link>
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii.likeCount ?? 0} inMiiPage />
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii._count.likedBy ?? 0} inMiiPage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ 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: {
|
||||
|
|
@ -97,6 +96,10 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop
|
|||
select: { userId: true },
|
||||
},
|
||||
}),
|
||||
// Like count
|
||||
_count: {
|
||||
select: { likedBy: true },
|
||||
},
|
||||
};
|
||||
|
||||
const skip = (page - 1) * limit;
|
||||
|
|
@ -108,7 +111,7 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop
|
|||
let orderBy: Prisma.MiiOrderByWithRelationInput[];
|
||||
|
||||
if (sort === "likes") {
|
||||
orderBy = [{ likeCount: "desc" }, { name: "asc" }];
|
||||
orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }];
|
||||
} else if (sort === "oldest") {
|
||||
orderBy = [{ createdAt: "asc" }, { name: "asc" }];
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) {
|
|||
</div>
|
||||
|
||||
<div className="mt-auto grid grid-cols-2 items-center">
|
||||
<LikeButton likes={mii.likeCount} miiId={mii.id} isLiked={likedIds.has(mii.id)} abbreviate />
|
||||
<LikeButton likes={mii._count.likedBy} miiId={mii.id} isLiked={likedIds.has(mii.id)} abbreviate />
|
||||
|
||||
{!userId && (
|
||||
<Link href={`/profile/${mii.user?.id}`} className="text-sm text-right overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
|
|
@ -87,7 +87,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) {
|
|||
<Link href={`/edit/${mii.id}`} title="Edit Mii" aria-label="Edit Mii" data-tooltip="Edit">
|
||||
<Icon icon="mdi:pencil" />
|
||||
</Link>
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii.likeCount} />
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii._count.likedBy} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ export default function MiiGrid({ miis, userId, parentPage }: Props) {
|
|||
<Icon icon="material-symbols:check-rounded" />
|
||||
</button>
|
||||
<div className="text-zinc-400 hover:text-red-500 transition-colors p-1 bg-white rounded-md shadow-sm border border-zinc-200 hover:border-red-500 flex items-center justify-center">
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii.likeCount} />
|
||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii._count.likedBy} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue