diff --git a/src/app/components/carousel.tsx b/src/app/components/carousel.tsx index db49c9e..82b150a 100644 --- a/src/app/components/carousel.tsx +++ b/src/app/components/carousel.tsx @@ -1,5 +1,6 @@ "use client"; +import Image from "next/image"; import { useCallback, useEffect, useState } from "react"; import useEmblaCarousel from "embla-carousel-react"; import { Icon } from "@iconify/react"; @@ -30,7 +31,7 @@ export default function Carousel({ images, className }: Props) {
{images.map((src, index) => (
- + mii image
))}
diff --git a/src/app/components/mii-list.tsx b/src/app/components/mii-list.tsx index 0420be6..0e76baf 100644 --- a/src/app/components/mii-list.tsx +++ b/src/app/components/mii-list.tsx @@ -11,7 +11,7 @@ interface Props { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; // for use on profiles userId?: number; - where?: Record; + where?: Record; } export default async function MiiList({ searchParams, userId, where }: Props) { diff --git a/src/app/components/submit-form.tsx b/src/app/components/submit-form.tsx index d612bc9..e8d5be9 100644 --- a/src/app/components/submit-form.tsx +++ b/src/app/components/submit-form.tsx @@ -17,6 +17,7 @@ import TagSelector from "./submit/tag-selector"; import ImageList from "./submit/image-list"; import QrUpload from "./submit/qr-upload"; import QrScanner from "./submit/qr-scanner"; +import Image from "next/image"; export default function SubmitForm() { const [files, setFiles] = useState([]); @@ -114,7 +115,7 @@ export default function SubmitForm() { generatedCode.make(); setGeneratedQrCodeUrl(generatedCode.createDataURL()); - } catch (error) { + } catch { setError("Failed to get and/or generate Mii images"); } }; @@ -128,11 +129,23 @@ export default function SubmitForm() {
{!studioUrl && Mii} - Nintendo Studio URL + Nintendo Studio URL
{!generatedQrCodeUrl && QR Code} - Generated QR Code + Generated QR Code
diff --git a/src/app/components/submit/image-list.tsx b/src/app/components/submit/image-list.tsx index 41faa49..e7f1d44 100644 --- a/src/app/components/submit/image-list.tsx +++ b/src/app/components/submit/image-list.tsx @@ -1,3 +1,4 @@ +import Image from "next/image"; import { FileWithPath } from "react-dropzone"; import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd"; import { Icon } from "@iconify/react"; @@ -37,7 +38,7 @@ export default function ImageList({ files, setFiles }: Props) { {...provided.draggableProps} className="w-full p-4 rounded-xl bg-orange-100 border-2 border-amber-500 flex gap-2 shadow-md my-1" > - {file.name} { if (!isOpen && !permissionGranted) return; requestRef.current = requestAnimationFrame(scanQRCode); - }, [permissionGranted]); + }, [isOpen, permissionGranted, scanQRCode]); if (!isOpen) return null; diff --git a/src/app/components/submit/qr-upload.tsx b/src/app/components/submit/qr-upload.tsx index cbb25e3..410a614 100644 --- a/src/app/components/submit/qr-upload.tsx +++ b/src/app/components/submit/qr-upload.tsx @@ -12,34 +12,37 @@ interface Props { export default function QrUpload({ setQrBytesRaw }: Props) { const canvasRef = useRef(null); - const onDrop = useCallback((acceptedFiles: FileWithPath[]) => { - acceptedFiles.forEach((file) => { - // Scan QR code - const reader = new FileReader(); - reader.onload = async (event) => { - const image = new Image(); - image.onload = () => { - const canvas = canvasRef.current; - if (!canvas) return; + const onDrop = useCallback( + (acceptedFiles: FileWithPath[]) => { + acceptedFiles.forEach((file) => { + // Scan QR code + const reader = new FileReader(); + reader.onload = async (event) => { + const image = new Image(); + image.onload = () => { + const canvas = canvasRef.current; + if (!canvas) return; - const ctx = canvas.getContext("2d"); - if (!ctx) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; - canvas.width = image.width; - canvas.height = image.height; - ctx.drawImage(image, 0, 0, image.width, image.height); + canvas.width = image.width; + canvas.height = image.height; + ctx.drawImage(image, 0, 0, image.width, image.height); - const imageData = ctx.getImageData(0, 0, image.width, image.height); - const code = jsQR(imageData.data, image.width, image.height); - if (!code) return; + const imageData = ctx.getImageData(0, 0, image.width, image.height); + const code = jsQR(imageData.data, image.width, image.height); + if (!code) return; - setQrBytesRaw(code.binaryData!); + setQrBytesRaw(code.binaryData!); + }; + image.src = event.target!.result as string; }; - image.src = event.target!.result as string; - }; - reader.readAsDataURL(file); - }); - }, []); + reader.readAsDataURL(file); + }); + }, + [setQrBytesRaw] + ); const { getRootProps, getInputProps } = useDropzone({ onDrop, diff --git a/src/app/components/submit/tag-selector.tsx b/src/app/components/submit/tag-selector.tsx index 780ad46..21d3ff2 100644 --- a/src/app/components/submit/tag-selector.tsx +++ b/src/app/components/submit/tag-selector.tsx @@ -1,7 +1,7 @@ "use client"; -import React, { useState, KeyboardEvent } from "react"; -import { useCombobox, useMultipleSelection } from "downshift"; +import React, { useState } from "react"; +import { useCombobox } from "downshift"; import { Icon } from "@iconify/react"; interface Props { @@ -135,7 +135,7 @@ export default function TagSelector({ tags, setTags }: Props) { setInputValue(""); }} > - Add "{inputValue}" + Add "{inputValue}" )} diff --git a/src/app/mii/[slug]/page.tsx b/src/app/mii/[slug]/page.tsx index e00c956..539e8e4 100644 --- a/src/app/mii/[slug]/page.tsx +++ b/src/app/mii/[slug]/page.tsx @@ -112,7 +112,7 @@ export default async function MiiPage({ params }: Props) { key={index} src={src} width={256} - height={1} + height={170} alt="mii screenshots" className="rounded-xl bg-zinc-300 border-2 border-zinc-300 shadow-md aspect-[3/2] h-full object-contain" /> diff --git a/src/app/page.tsx b/src/app/page.tsx index a580911..e729576 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,14 +9,5 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [ redirect("/create-username"); } - // await prisma.mii.create({ - // data: { - // userId: 1, - // name: "Himmel", - // pictures: ["https://placehold.co/600x400", "/missing.webp"], - // tags: ["Anime", "Osaka"], - // }, - // }); - return ; } diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx index a557a54..9e73528 100644 --- a/src/app/privacy/page.tsx +++ b/src/app/privacy/page.tsx @@ -89,8 +89,8 @@ export default function PrivacyPage() {

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, - your data will be promptly removed from our servers. + deleted at any time by going to your profile page, clicking the settings icon, and clicking the 'Delete Account' button. Upon + clicking, your data will be promptly removed from our servers.

diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c23381c..23c12b9 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -25,7 +25,7 @@ export default async function SearchPage({ searchParams }: Props) { return (

- Search results for "{query}" + Search results for "{query}"

Privacy Policy {" "} - (see "Data Deletion") or email me at{" "} + (see "Data Deletion") or email me at{" "} hello@trafficlunar.net @@ -70,8 +70,8 @@ export default function PrivacyPage() {

- This service is provided "as is" and without any warranties. We are not responsible for any user-generated content or the actions of - users on the site. You use the site at your own risk. + This service is provided "as is" and without any warranties. We are not responsible for any user-generated content or the + actions of users on the site. You use the site at your own risk.

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() {

- This site is not affiliated with, endorsed by, or associated with Nintendo in any way. "Mii" and all related character designs are - trademarks of Nintendo Co., Ltd. + This site is not affiliated with, endorsed by, or associated with Nintendo in any way. "Mii" and all related character designs + are trademarks of Nintendo Co., Ltd.

All Mii-related content is shared by users under the assumption that it does not violate any third-party rights. If you believe your diff --git a/src/lib/images.ts b/src/lib/images.ts index 791c405..3da7a31 100644 --- a/src/lib/images.ts +++ b/src/lib/images.ts @@ -6,7 +6,7 @@ const MIN_IMAGE_DIMENSIONS = 128; const MAX_IMAGE_DIMENSIONS = 1024; const MAX_IMAGE_SIZE = 1024 * 1024; // 1 MB -const THRESHOLD = 0.5; +// const THRESHOLD = 0.5; // tf.enableProdMode(); diff --git a/src/lib/qr-codes.ts b/src/lib/qr-codes.ts index c47b370..bcb8802 100644 --- a/src/lib/qr-codes.ts +++ b/src/lib/qr-codes.ts @@ -15,7 +15,7 @@ export function convertQrCode(bytes: Uint8Array): { mii: Mii; tomodachiLifeMii: let decrypted: Uint8Array = new Uint8Array(); try { 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"); } @@ -40,7 +40,7 @@ export function convertQrCode(bytes: Uint8Array): { mii: Mii; tomodachiLifeMii: } return { mii, tomodachiLifeMii }; - } catch (error) { + } catch { throw new Error("Mii data is not valid"); } } diff --git a/src/types.d.ts b/src/types.d.ts index a2705f5..0b3841a 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,5 +1,4 @@ -import { DefaultSession, User } from "next-auth"; -import { User as PrismaUser } from "@prisma/client"; +import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session {