mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-06-28 14:44:15 +00:00
feat: makeup filter, bad_quality report type
This commit is contained in:
parent
4f03d611eb
commit
fd11f996df
15 changed files with 233 additions and 15 deletions
|
|
@ -4,12 +4,13 @@ import { useSearchParams } from "next/navigation";
|
|||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
import { MiiGender, MiiPlatform } from "@prisma/client";
|
||||
import { MiiGender, MiiMakeup, MiiPlatform } from "@prisma/client";
|
||||
|
||||
import PlatformSelect from "./platform-select";
|
||||
import TagFilter from "./tag-filter";
|
||||
import GenderSelect from "./gender-select";
|
||||
import OtherFilters from "./other-filters";
|
||||
import MakeupSelect from "./makeup-select";
|
||||
|
||||
export default function FilterMenu() {
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -19,6 +20,7 @@ export default function FilterMenu() {
|
|||
|
||||
const platform = (searchParams.get("platform") as MiiPlatform) || undefined;
|
||||
const gender = (searchParams.get("gender") as MiiGender) || undefined;
|
||||
const makeup = (searchParams.get("makeup") as MiiMakeup) || undefined;
|
||||
const rawTags = searchParams.get("tags") || "";
|
||||
const rawExclude = searchParams.get("exclude") || "";
|
||||
const allowCopying = (searchParams.get("allowCopying") as unknown as boolean) || false;
|
||||
|
|
@ -66,9 +68,10 @@ export default function FilterMenu() {
|
|||
if (platform) count++;
|
||||
if (gender) count++;
|
||||
if (allowCopying) count++;
|
||||
if (makeup) count++;
|
||||
|
||||
setFilterCount(count);
|
||||
}, [tags, exclude, platform, gender, allowCopying]);
|
||||
}, [tags, exclude, platform, gender, allowCopying, makeup]);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
|
@ -114,6 +117,16 @@ export default function FilterMenu() {
|
|||
</div>
|
||||
<TagFilter isExclude />
|
||||
|
||||
{platform !== "THREE_DS" && (
|
||||
<>
|
||||
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium w-full mt-2 mb-1">
|
||||
<hr className="grow border-zinc-300" />
|
||||
<span>Makeup</span>
|
||||
<hr className="grow border-zinc-300" />
|
||||
</div>
|
||||
<MakeupSelect />
|
||||
</>
|
||||
)}
|
||||
{platform !== "SWITCH" && (
|
||||
<>
|
||||
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium w-full mt-2 mb-1">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { headers } from "next/headers";
|
||||
import Link from "next/link";
|
||||
|
||||
import { MiiGender, MiiPlatform, Prisma } from "@prisma/client";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
import crypto from "crypto";
|
||||
|
|
@ -29,7 +28,7 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
|||
const parsed = searchSchema.safeParse(searchParams);
|
||||
if (!parsed.success) return <h1>{parsed.error.issues[0].message}</h1>;
|
||||
|
||||
const { q: query, sort, tags, exclude, platform, gender, allowCopying, page = 1, limit = 24, seed } = parsed.data;
|
||||
const { q: query, sort, tags, exclude, platform, gender, makeup, allowCopying, page = 1, limit = 24, seed } = parsed.data;
|
||||
|
||||
// My Likes page
|
||||
let miiIdsLiked: number[] | undefined = undefined;
|
||||
|
|
@ -58,6 +57,8 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
|||
...(gender && { gender: { equals: gender } }),
|
||||
// Allow Copying
|
||||
...(allowCopying && { allowedCopying: true }),
|
||||
// Makeup
|
||||
...(makeup && { makeup: { equals: makeup } }),
|
||||
// Profiles
|
||||
...(userId && { userId }),
|
||||
};
|
||||
|
|
@ -79,6 +80,7 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
|||
tags: true,
|
||||
createdAt: true,
|
||||
gender: true,
|
||||
makeup: true,
|
||||
allowedCopying: true,
|
||||
// Mii liked check
|
||||
...(session?.user?.id && {
|
||||
|
|
|
|||
75
src/components/mii/list/makeup-select.tsx
Normal file
75
src/components/mii/list/makeup-select.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
"use client";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { MiiMakeup, MiiPlatform } from "@prisma/client";
|
||||
|
||||
export default function MakeupSelect() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
const [selected, setSelected] = useState<MiiMakeup | null>((searchParams.get("makeup") as MiiMakeup) ?? null);
|
||||
|
||||
const handleClick = (makeup: MiiMakeup) => {
|
||||
const filter = selected === makeup ? null : makeup;
|
||||
setSelected(filter);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
||||
if (filter) {
|
||||
params.set("makeup", filter);
|
||||
} else {
|
||||
params.delete("makeup");
|
||||
}
|
||||
|
||||
startTransition(() => {
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex gap-0.5 w-fit">
|
||||
{/* Full Makeup */}
|
||||
<button
|
||||
onClick={() => handleClick("FULL")}
|
||||
aria-label="Filter for Full Makeup"
|
||||
data-tooltip-span
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-13 text-5xl border-2 transition-all ${
|
||||
selected === "FULL" ? "bg-pink-100 border-pink-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<div className="tooltip bg-pink-400! border-pink-400! before:border-b-pink-400!">Full Makeup</div>
|
||||
<Icon icon="mdi:palette" className="text-pink-400" />
|
||||
</button>
|
||||
|
||||
{/* Partial Makeup */}
|
||||
<button
|
||||
onClick={() => handleClick("PARTIAL")}
|
||||
aria-label="Filter for Partial Makeup"
|
||||
data-tooltip-span
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-13 text-5xl border-2 transition-all ${
|
||||
selected === "PARTIAL" ? "bg-purple-100 border-purple-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<div className="tooltip bg-purple-400! border-purple-400! before:border-b-purple-400!">Partial Makeup</div>
|
||||
<Icon icon="mdi:lipstick" className="text-purple-400" />
|
||||
</button>
|
||||
|
||||
{/* No Makeup */}
|
||||
<button
|
||||
onClick={() => handleClick("NONE")}
|
||||
aria-label="Filter for No Makeup"
|
||||
data-tooltip-span
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-13 text-5xl border-2 transition-all ${
|
||||
selected === "NONE" ? "bg-gray-200 border-gray-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<div className="tooltip bg-gray-400! border-gray-400! before:border-b-gray-400!">No Makeup</div>
|
||||
<Icon icon="codex:cross" className="text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ const reasonMap: Record<ReportReason, string> = {
|
|||
INAPPROPRIATE: "Inappropriate content",
|
||||
SPAM: "Spam",
|
||||
COPYRIGHT: "Copyrighted content",
|
||||
BAD_QUALITY: "Bad quality",
|
||||
OTHER: "Other...",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { FileWithPath } from "react-dropzone";
|
|||
import { Icon } from "@iconify/react";
|
||||
|
||||
import qrcode from "qrcode-generator";
|
||||
import { MiiGender, MiiPlatform } from "@prisma/client";
|
||||
import { MiiGender, MiiMakeup, MiiPlatform } from "@prisma/client";
|
||||
|
||||
import { nameSchema, tagsSchema } from "@/lib/schemas";
|
||||
import { convertQrCode } from "@/lib/qr-codes";
|
||||
|
|
@ -52,6 +52,7 @@ export default function SubmitForm() {
|
|||
|
||||
const [platform, setPlatform] = useState<MiiPlatform>("SWITCH");
|
||||
const [gender, setGender] = useState<MiiGender>("MALE");
|
||||
const [makeup, setMakeup] = useState<MiiMakeup>("NONE");
|
||||
const instructions = useRef<SwitchMiiInstructions>(defaultInstructions);
|
||||
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
|
|
@ -99,6 +100,7 @@ export default function SubmitForm() {
|
|||
}
|
||||
|
||||
formData.append("gender", gender);
|
||||
formData.append("makeup", makeup);
|
||||
formData.append("miiPortraitImage", portraitBlob);
|
||||
formData.append("miiFeaturesImage", featuresBlob);
|
||||
formData.append("instructions", JSON.stringify(instructions.current));
|
||||
|
|
@ -326,6 +328,54 @@ export default function SubmitForm() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Makeup (switch only) */}
|
||||
<div className={`w-full grid grid-cols-3 items-start ${platform === "SWITCH" ? "" : "hidden"}`}>
|
||||
<label htmlFor="makeup" className="font-semibold py-2">
|
||||
Makeup
|
||||
</label>
|
||||
|
||||
<div className="col-span-2 flex gap-1">
|
||||
{/* Full Makeup */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMakeup("FULL")}
|
||||
aria-label="Full makeup"
|
||||
data-tooltip="Full Makeup"
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-11 text-4xl border-2 transition-all after:bg-pink-400! after:border-pink-400! before:border-b-pink-400! ${
|
||||
makeup === "FULL" ? "bg-pink-100 border-pink-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<Icon icon="mdi:palette" className="text-pink-400" />
|
||||
</button>
|
||||
|
||||
{/* Partial Makeup */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMakeup("PARTIAL")}
|
||||
aria-label="Partial makeup"
|
||||
data-tooltip="Partial Makeup"
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-11 text-4xl border-2 transition-all after:bg-purple-400! after:border-purple-400! before:border-b-purple-400! ${
|
||||
makeup === "PARTIAL" ? "bg-purple-100 border-purple-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<Icon icon="mdi:lipstick" className="text-purple-400" />
|
||||
</button>
|
||||
|
||||
{/* No Makeup */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMakeup("NONE")}
|
||||
aria-label="No makeup"
|
||||
data-tooltip="No Makeup"
|
||||
className={`cursor-pointer rounded-xl flex justify-center items-center size-11 text-4xl border-2 transition-all after:bg-gray-400! after:border-gray-400! before:border-b-gray-400! ${
|
||||
makeup === "NONE" ? "bg-gray-200 border-gray-400 shadow-md" : "bg-white border-gray-300 hover:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
<Icon icon="codex:cross" className="text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* (Switch Only) Mii Portrait */}
|
||||
<div className={`${platform === "SWITCH" ? "" : "hidden"}`}>
|
||||
{/* Separator */}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue