fix: build errors

This commit is contained in:
trafficlunar 2025-04-09 13:49:09 +01:00
parent 4a65994d35
commit 516923905d
15 changed files with 65 additions and 57 deletions

View file

@ -1,5 +1,6 @@
"use client"; "use client";
import Image from "next/image";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import useEmblaCarousel from "embla-carousel-react"; import useEmblaCarousel from "embla-carousel-react";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
@ -30,7 +31,7 @@ export default function Carousel({ images, className }: Props) {
<div className="flex"> <div className="flex">
{images.map((src, index) => ( {images.map((src, index) => (
<div key={index} className="flex-[0_0_100%]"> <div key={index} className="flex-[0_0_100%]">
<img src={src} alt="" className="w-full h-auto aspect-[3/2] object-contain" /> <Image src={src} alt="mii image" width={480} height={320} className="w-full h-auto aspect-[3/2] object-contain" />
</div> </div>
))} ))}
</div> </div>

View file

@ -11,7 +11,7 @@ interface Props {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
// for use on profiles // for use on profiles
userId?: number; userId?: number;
where?: Record<string, any>; where?: Record<string, object>;
} }
export default async function MiiList({ searchParams, userId, where }: Props) { export default async function MiiList({ searchParams, userId, where }: Props) {

View file

@ -17,6 +17,7 @@ import TagSelector from "./submit/tag-selector";
import ImageList from "./submit/image-list"; import ImageList from "./submit/image-list";
import QrUpload from "./submit/qr-upload"; import QrUpload from "./submit/qr-upload";
import QrScanner from "./submit/qr-scanner"; import QrScanner from "./submit/qr-scanner";
import Image from "next/image";
export default function SubmitForm() { export default function SubmitForm() {
const [files, setFiles] = useState<FileWithPath[]>([]); const [files, setFiles] = useState<FileWithPath[]>([]);
@ -114,7 +115,7 @@ export default function SubmitForm() {
generatedCode.make(); generatedCode.make();
setGeneratedQrCodeUrl(generatedCode.createDataURL()); setGeneratedQrCodeUrl(generatedCode.createDataURL());
} catch (error) { } catch {
setError("Failed to get and/or generate Mii images"); setError("Failed to get and/or generate Mii images");
} }
}; };
@ -128,11 +129,23 @@ export default function SubmitForm() {
<div className="flex justify-center gap-2"> <div className="flex justify-center gap-2">
<div className="relative flex justify-center items-center size-32 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500"> <div className="relative flex justify-center items-center size-32 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500">
{!studioUrl && <span className="absolute text-center font-light">Mii</span>} {!studioUrl && <span className="absolute text-center font-light">Mii</span>}
<img src={studioUrl} alt="Nintendo Studio URL" className="size-full rounded-xl text-[0px]" /> <Image
src={studioUrl ?? "/missing.webp"}
width={128}
height={128}
alt="Nintendo Studio URL"
className="size-full rounded-xl text-[0px]"
/>
</div> </div>
<div className="relative flex justify-center items-center size-32 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500"> <div className="relative flex justify-center items-center size-32 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500">
{!generatedQrCodeUrl && <span className="absolute text-center font-light">QR Code</span>} {!generatedQrCodeUrl && <span className="absolute text-center font-light">QR Code</span>}
<img src={generatedQrCodeUrl} alt="Generated QR Code" className="size-full rounded-xl text-[0px]" /> <Image
src={generatedQrCodeUrl ?? "/missing.webp"}
width={128}
height={128}
alt="Generated QR Code"
className="size-full rounded-xl text-[0px]"
/>
</div> </div>
</div> </div>

View file

@ -1,3 +1,4 @@
import Image from "next/image";
import { FileWithPath } from "react-dropzone"; import { FileWithPath } from "react-dropzone";
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd"; import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
@ -37,7 +38,7 @@ export default function ImageList({ files, setFiles }: Props) {
{...provided.draggableProps} {...provided.draggableProps}
className="w-full p-4 rounded-xl bg-orange-100 border-2 border-amber-500 flex gap-2 shadow-md my-1" className="w-full p-4 rounded-xl bg-orange-100 border-2 border-amber-500 flex gap-2 shadow-md my-1"
> >
<img <Image
src={URL.createObjectURL(file)} src={URL.createObjectURL(file)}
alt={file.name} alt={file.name}
className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400" className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400"

View file

@ -97,12 +97,12 @@ export default function QrScanner({ isOpen, setIsOpen, setQrBytesRaw }: Props) {
setSelectedDeviceId(videoDevices[0].deviceId); setSelectedDeviceId(videoDevices[0].deviceId);
} }
}); });
}, [isOpen]); }, [isOpen, selectedDeviceId]);
useEffect(() => { useEffect(() => {
if (!isOpen && !permissionGranted) return; if (!isOpen && !permissionGranted) return;
requestRef.current = requestAnimationFrame(scanQRCode); requestRef.current = requestAnimationFrame(scanQRCode);
}, [permissionGranted]); }, [isOpen, permissionGranted, scanQRCode]);
if (!isOpen) return null; if (!isOpen) return null;

View file

@ -12,7 +12,8 @@ interface Props {
export default function QrUpload({ setQrBytesRaw }: Props) { export default function QrUpload({ setQrBytesRaw }: Props) {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const onDrop = useCallback((acceptedFiles: FileWithPath[]) => { const onDrop = useCallback(
(acceptedFiles: FileWithPath[]) => {
acceptedFiles.forEach((file) => { acceptedFiles.forEach((file) => {
// Scan QR code // Scan QR code
const reader = new FileReader(); const reader = new FileReader();
@ -39,7 +40,9 @@ export default function QrUpload({ setQrBytesRaw }: Props) {
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
}, []); },
[setQrBytesRaw]
);
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop, onDrop,

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useState, KeyboardEvent } from "react"; import React, { useState } from "react";
import { useCombobox, useMultipleSelection } from "downshift"; import { useCombobox } from "downshift";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
interface Props { interface Props {
@ -135,7 +135,7 @@ export default function TagSelector({ tags, setTags }: Props) {
setInputValue(""); setInputValue("");
}} }}
> >
Add "{inputValue}" Add &quot;{inputValue}&quot;
</li> </li>
)} )}
</ul> </ul>

View file

@ -112,7 +112,7 @@ export default async function MiiPage({ params }: Props) {
key={index} key={index}
src={src} src={src}
width={256} width={256}
height={1} height={170}
alt="mii screenshots" alt="mii screenshots"
className="rounded-xl bg-zinc-300 border-2 border-zinc-300 shadow-md aspect-[3/2] h-full object-contain" className="rounded-xl bg-zinc-300 border-2 border-zinc-300 shadow-md aspect-[3/2] h-full object-contain"
/> />

View file

@ -9,14 +9,5 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [
redirect("/create-username"); redirect("/create-username");
} }
// await prisma.mii.create({
// data: {
// userId: 1,
// name: "Himmel",
// pictures: ["https://placehold.co/600x400", "/missing.webp"],
// tags: ["Anime", "Osaka"],
// },
// });
return <MiiList searchParams={searchParams} />; return <MiiList searchParams={searchParams} />;
} }

View file

@ -89,8 +89,8 @@ export default function PrivacyPage() {
<section> <section>
<p className="mb-2"> <p className="mb-2">
Your data, including your Miis, will be retained for as long as you have an account on the site. You may request that your data be Your data, including your Miis, will be retained for as long as you have an account on the site. You may request that your data be
deleted at any time by going to your profile page, clicking the settings icon, and clicking the 'Delete Account' button. Upon clicking, deleted at any time by going to your profile page, clicking the settings icon, and clicking the &apos;Delete Account&apos; button. Upon
your data will be promptly removed from our servers. clicking, your data will be promptly removed from our servers.
</p> </p>
</section> </section>
</li> </li>

View file

@ -25,7 +25,7 @@ export default async function SearchPage({ searchParams }: Props) {
return ( return (
<div> <div>
<p className="text-lg"> <p className="text-lg">
Search results for "<span className="font-bold">{query}</span>" Search results for &quot;<span className="font-bold">{query}</span>&quot;
</p> </p>
<MiiList <MiiList
searchParams={searchParams} searchParams={searchParams}

View file

@ -51,7 +51,7 @@ export default function PrivacyPage() {
<a href="/privacy" className="text-blue-700"> <a href="/privacy" className="text-blue-700">
Privacy Policy Privacy Policy
</a>{" "} </a>{" "}
(see "Data Deletion") or email me at{" "} (see &quot;Data Deletion&quot;) or email me at{" "}
<a href="mailto:hello@trafficlunar.net" className="text-blue-700"> <a href="mailto:hello@trafficlunar.net" className="text-blue-700">
hello@trafficlunar.net hello@trafficlunar.net
</a> </a>
@ -70,8 +70,8 @@ export default function PrivacyPage() {
<section> <section>
<p className="mb-2"> <p className="mb-2">
This service is provided "as is" and without any warranties. We are not responsible for any user-generated content or the actions of This service is provided &quot;as is&quot; and without any warranties. We are not responsible for any user-generated content or the
users on the site. You use the site at your own risk. actions of users on the site. You use the site at your own risk.
</p> </p>
<p> <p>
We do not guarantee continuous or secure access to the service and are not liable for any damages resulting from interruptions, loss of We do not guarantee continuous or secure access to the service and are not liable for any damages resulting from interruptions, loss of
@ -109,8 +109,8 @@ export default function PrivacyPage() {
<section> <section>
<p className="mb-2"> <p className="mb-2">
This site is not affiliated with, endorsed by, or associated with Nintendo in any way. "Mii" and all related character designs are This site is not affiliated with, endorsed by, or associated with Nintendo in any way. &quot;Mii&quot; and all related character designs
trademarks of Nintendo Co., Ltd. are trademarks of Nintendo Co., Ltd.
</p> </p>
<p> <p>
All Mii-related content is shared by users under the assumption that it does not violate any third-party rights. If you believe your All Mii-related content is shared by users under the assumption that it does not violate any third-party rights. If you believe your

View file

@ -6,7 +6,7 @@ const MIN_IMAGE_DIMENSIONS = 128;
const MAX_IMAGE_DIMENSIONS = 1024; const MAX_IMAGE_DIMENSIONS = 1024;
const MAX_IMAGE_SIZE = 1024 * 1024; // 1 MB const MAX_IMAGE_SIZE = 1024 * 1024; // 1 MB
const THRESHOLD = 0.5; // const THRESHOLD = 0.5;
// tf.enableProdMode(); // tf.enableProdMode();

View file

@ -15,7 +15,7 @@ export function convertQrCode(bytes: Uint8Array): { mii: Mii; tomodachiLifeMii:
let decrypted: Uint8Array<ArrayBufferLike> = new Uint8Array(); let decrypted: Uint8Array<ArrayBufferLike> = new Uint8Array();
try { try {
decrypted = AES_CCM.decrypt(content, MII_DECRYPTION_KEY, nonceWithZeros, undefined, 16); decrypted = AES_CCM.decrypt(content, MII_DECRYPTION_KEY, nonceWithZeros, undefined, 16);
} catch (error) { } catch {
throw new Error("Failed to decrypt QR code. It may be invalid or corrupted"); throw new Error("Failed to decrypt QR code. It may be invalid or corrupted");
} }
@ -40,7 +40,7 @@ export function convertQrCode(bytes: Uint8Array): { mii: Mii; tomodachiLifeMii:
} }
return { mii, tomodachiLifeMii }; return { mii, tomodachiLifeMii };
} catch (error) { } catch {
throw new Error("Mii data is not valid"); throw new Error("Mii data is not valid");
} }
} }

3
src/types.d.ts vendored
View file

@ -1,5 +1,4 @@
import { DefaultSession, User } from "next-auth"; import { DefaultSession } from "next-auth";
import { User as PrismaUser } from "@prisma/client";
declare module "next-auth" { declare module "next-auth" {
interface Session { interface Session {