diff --git a/package.json b/package.json
index 624eac6..a226af1 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,6 @@
"@bprogress/next": "^3.2.12",
"@hello-pangea/dnd": "^18.0.1",
"@prisma/client": "^6.11.1",
- "@types/sjcl": "^1.0.34",
"bit-buffer": "^0.2.5",
"canvas-confetti": "^1.9.3",
"dayjs": "^1.11.13",
@@ -47,6 +46,7 @@
"@types/node": "^24.0.13",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
+ "@types/sjcl": "^1.0.34",
"eslint": "^9.31.0",
"eslint-config-next": "15.3.5",
"prisma": "^6.11.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 940af48..390b290 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,9 +23,6 @@ importers:
'@prisma/client':
specifier: ^6.11.1
version: 6.11.1(prisma@6.11.1(typescript@5.8.3))(typescript@5.8.3)
- '@types/sjcl':
- specifier: ^1.0.34
- version: 1.0.34
bit-buffer:
specifier: ^0.2.5
version: 0.2.5
@@ -108,6 +105,9 @@ importers:
'@types/react-dom':
specifier: ^19.1.6
version: 19.1.6(@types/react@19.1.8)
+ '@types/sjcl':
+ specifier: ^1.0.34
+ version: 1.0.34
eslint:
specifier: ^9.31.0
version: 9.31.0(jiti@2.4.2)
diff --git a/src/app/profile/[id]/page.tsx b/src/app/profile/[id]/page.tsx
index 66e512e..d621992 100644
--- a/src/app/profile/[id]/page.tsx
+++ b/src/app/profile/[id]/page.tsx
@@ -78,11 +78,9 @@ export default async function ProfilePage({ searchParams, params }: Props) {
return (
);
}
diff --git a/src/app/profile/likes/page.tsx b/src/app/profile/likes/page.tsx
index 98fa49e..abce75e 100644
--- a/src/app/profile/likes/page.tsx
+++ b/src/app/profile/likes/page.tsx
@@ -29,20 +29,16 @@ export default async function ProfileSettingsPage({ searchParams }: Props) {
return (
-
+
My Likes
View every Mii you have liked on TomodachiShare.
-
-
-
-
-
-
}>
-
-
+
+
}>
+
+
);
}
diff --git a/src/components/mii-list/filter-select.tsx b/src/components/mii-list/filter-select.tsx
deleted file mode 100644
index 3f1238f..0000000
--- a/src/components/mii-list/filter-select.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-"use client";
-
-import { redirect, useSearchParams } from "next/navigation";
-import { useEffect, useMemo, useState } from "react";
-import TagSelector from "../tag-selector";
-
-export default function FilterSelect() {
- const searchParams = useSearchParams();
-
- const rawTags = searchParams.get("tags");
- const preexistingTags = useMemo(
- () =>
- rawTags
- ? rawTags
- .split(",")
- .map((tag) => tag.trim())
- .filter((tag) => tag.length > 0)
- : [],
- [rawTags]
- );
-
- const [isOpen, setIsOpen] = useState(false);
- const [tags, setTags] = useState
(preexistingTags);
-
- const handleSubmit = () => {
- redirect(`/?tags=${encodeURIComponent(tags.join(","))}`);
- };
-
- useEffect(() => {
- setTags(preexistingTags);
- }, [preexistingTags]);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/mii-list/gender-select.tsx b/src/components/mii-list/gender-select.tsx
new file mode 100644
index 0000000..25997cf
--- /dev/null
+++ b/src/components/mii-list/gender-select.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { useRouter, useSearchParams } from "next/navigation";
+import { useState, useTransition } from "react";
+import { Icon } from "@iconify/react";
+import { MiiGender } from "@prisma/client";
+
+export default function GenderSelect() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const [, startTransition] = useTransition();
+
+ const [selected, setSelected] = useState((searchParams.get("gender") as MiiGender) ?? null);
+
+ const handleClick = (gender: MiiGender) => {
+ const filter = selected === gender ? null : gender;
+ setSelected(filter);
+
+ const params = new URLSearchParams(searchParams);
+ if (filter) {
+ params.set("gender", filter);
+ } else {
+ params.delete("gender");
+ }
+
+ startTransition(() => {
+ router.push(`?${params.toString()}`);
+ });
+ };
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/components/mii-list/index.tsx b/src/components/mii-list/index.tsx
index 82d943d..a01c318 100644
--- a/src/components/mii-list/index.tsx
+++ b/src/components/mii-list/index.tsx
@@ -1,6 +1,6 @@
import Link from "next/link";
-import { Prisma } from "@prisma/client";
+import { MiiGender, Prisma } from "@prisma/client";
import { Icon } from "@iconify/react";
import { z } from "zod";
@@ -8,7 +8,8 @@ import { querySchema } from "@/lib/schemas";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
-import FilterSelect from "./filter-select";
+import GenderSelect from "./gender-select";
+import TagFilter from "./tag-filter";
import SortSelect from "./sort-select";
import Carousel from "../carousel";
import LikeButton from "../like-button";
@@ -33,6 +34,7 @@ const searchSchema = z.object({
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0)
),
+ gender: z.enum(MiiGender, { error: "Gender must be either 'MALE', or 'FEMALE'" }).optional(),
// todo: incorporate tagsSchema
// Pages
limit: z.coerce
@@ -54,8 +56,9 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
const parsed = searchSchema.safeParse(searchParams);
if (!parsed.success) return {parsed.error.issues[0].message}
;
- const { q: query, sort, tags, page = 1, limit = 24 } = parsed.data;
+ const { q: query, sort, tags, gender, page = 1, limit = 24 } = parsed.data;
+ // My Likes page
let miiIdsLiked: number[] | undefined = undefined;
if (inLikesPage && session?.user.id) {
@@ -75,6 +78,8 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
}),
// Tag filtering
...(tags && tags.length > 0 && { tags: { hasEvery: tags } }),
+ // Gender
+ ...(gender && { gender: { equals: gender } }),
// Profiles
...(userId && { userId }),
};
@@ -136,8 +141,8 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
return (
-
-
+
+
{totalCount == filteredCount ? (
<>
{totalCount} Miis
@@ -149,8 +154,9 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
)}
-
diff --git a/src/components/mii-list/skeleton.tsx b/src/components/mii-list/skeleton.tsx
index 9747ecc..3ec8ee5 100644
--- a/src/components/mii-list/skeleton.tsx
+++ b/src/components/mii-list/skeleton.tsx
@@ -1,4 +1,4 @@
-import FilterSelect from "./filter-select";
+import FilterSelect from "./tag-filter";
import SortSelect from "./sort-select";
import Pagination from "./pagination";
diff --git a/src/components/mii-list/sort-select.tsx b/src/components/mii-list/sort-select.tsx
index 90d8fcf..828dd0c 100644
--- a/src/components/mii-list/sort-select.tsx
+++ b/src/components/mii-list/sort-select.tsx
@@ -1,15 +1,18 @@
"use client";
-import { Icon } from "@iconify/react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useTransition } from "react";
import { useSelect } from "downshift";
-import { redirect, useSearchParams } from "next/navigation";
+import { Icon } from "@iconify/react";
type Sort = "newest" | "likes" | "oldest";
const items = ["newest", "likes", "oldest"];
export default function SortSelect() {
+ const router = useRouter();
const searchParams = useSearchParams();
+ const [, startTransition] = useTransition();
const currentSort = (searchParams.get("sort") as Sort) || "newest";
@@ -21,12 +24,15 @@ export default function SortSelect() {
const params = new URLSearchParams(searchParams);
params.set("sort", selectedItem);
- redirect(`?${params.toString()}`);
+
+ startTransition(() => {
+ router.push(`?${params.toString()}`);
+ });
},
});
return (
-
+
{/* Toggle button to open the dropdown */}