import { Prisma } from "@prisma/client"; import crypto from "crypto"; import seedrandom from "seedrandom"; import { searchSchema } from "@/lib/schemas"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import SortSelect from "./sort-select"; import Pagination from "./pagination"; import FilterMenu from "./filter-menu"; import MiiGrid from "./mii-grid"; interface Props { searchParams: { [key: string]: string | string[] | undefined }; userId?: number; // Profiles inLikesPage?: boolean; // Self-explanatory } export default async function MiiList({ searchParams, userId, inLikesPage }: Props) { const session = await auth(); const parsed = searchSchema.safeParse(searchParams); if (!parsed.success) return

{parsed.error.issues[0].message}

; const { q: query, sort, tags, exclude, platform, gender, makeup, allowCopying, quarantined, page = 1, limit = 24, seed } = parsed.data; // My Likes page let miiIdsLiked: number[] | undefined = undefined; if (inLikesPage && session?.user?.id) { const likedMiis = await prisma.like.findMany({ where: { userId: Number(session.user.id) }, select: { miiId: true }, }); miiIdsLiked = likedMiis.map((like) => like.miiId); } const where: Prisma.MiiWhereInput = { in_queue: false, // Only show liked miis on likes page ...(inLikesPage && miiIdsLiked && { id: { in: miiIdsLiked } }), // Searching ...(query && { OR: [{ name: { contains: query, mode: "insensitive" } }, { tags: { has: query } }, { description: { contains: query, mode: "insensitive" } }], }), // Tag filtering ...(tags && tags.length > 0 && { tags: { hasEvery: tags } }), ...(exclude && exclude.length > 0 && { NOT: { tags: { hasSome: exclude } } }), // Platform ...(platform && { platform: { equals: platform } }), // Gender ...(gender && { gender: { equals: gender } }), // Allow Copying ...(allowCopying && { allowedCopying: true }), // Makeup ...(makeup && { makeup: { equals: makeup } }), // Quarantined ...(!quarantined && !userId && { quarantined: false }), // Profiles ...(userId && { userId }), }; const select: Prisma.MiiSelect = { id: true, // Don't show when userId is specified ...(!userId && { user: { select: { id: true, name: true, }, }, }), platform: true, name: true, imageCount: true, tags: true, createdAt: true, gender: true, makeup: true, allowedCopying: true, quarantined: true, // Mii liked check ...(session?.user?.id && { likedBy: { where: { userId: Number(session.user.id) }, select: { userId: true }, }, }), // Like count _count: { select: { likedBy: true }, }, }; const skip = (page - 1) * limit; let totalCount: number; let filteredCount: number; let miis: Prisma.MiiGetPayload<{ select: typeof select }>[]; if (sort === "random") { // Get all IDs that match the where conditions const matchingIds = await prisma.mii.findMany({ where, select: { id: true }, }); totalCount = matchingIds.length; filteredCount = Math.max(0, Math.min(limit, totalCount - skip)); if (matchingIds.length === 0) return; // Use seed for consistent random results const randomSeed = seed || crypto.randomInt(0, 1_000_000_000); const rng = seedrandom(randomSeed.toString()); // Randomize all IDs using the Durstenfeld algorithm for (let i = matchingIds.length - 1; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); [matchingIds[i], matchingIds[j]] = [matchingIds[j], matchingIds[i]]; } // Convert to number[] array const selectedIds = matchingIds.slice(skip, skip + limit).map((i) => i.id); miis = await prisma.mii.findMany({ where: { id: { in: selectedIds }, }, select, }); } else { // Sorting by likes, newest, or oldest let orderBy: Prisma.MiiOrderByWithRelationInput[]; if (sort === "likes") { orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }]; } else if (sort === "oldest") { orderBy = [{ createdAt: "asc" }, { name: "asc" }]; } else { // default to newest orderBy = [{ createdAt: "desc" }, { name: "asc" }]; } [totalCount, filteredCount, miis] = await Promise.all([ prisma.mii.count({ where: { ...where, userId } }), prisma.mii.count({ where, skip, take: limit }), prisma.mii.findMany({ where, orderBy, select, skip: (page - 1) * limit, take: limit, }), ]); } const lastPage = Math.ceil(totalCount / limit); return (
{totalCount == filteredCount ? ( <> {totalCount} {totalCount === 1 ? "Mii" : "Miis"} ) : ( <> {filteredCount} of {totalCount} Miis )}
); }