diff --git a/src/app/api/submit/route.ts b/src/app/api/submit/route.ts index b3f1ed0..a68783f 100644 --- a/src/app/api/submit/route.ts +++ b/src/app/api/submit/route.ts @@ -91,6 +91,34 @@ export async function POST(request: NextRequest) { return rateLimit.sendResponse({ error: "Invalid JSON in tags or QR code data" }, 400); } + // Minify instructions to save space and improve user experience + let minifiedInstructions: Partial | undefined; + if (formData.get("platform") === "SWITCH") { + function minify(object: Partial): Partial { + for (const key in object) { + const value = object[key as keyof SwitchMiiInstructions]; + + if (!value) { + delete object[key as keyof SwitchMiiInstructions]; + continue; + } + + // Recurse into nested objects + if (typeof value === "object") { + minify(value as Partial); + + if (Object.keys(value).length === 0) { + delete object[key as keyof SwitchMiiInstructions]; + } + } + } + + return object; + } + + minifiedInstructions = minify(JSON.parse((formData.get("instructions") as string) ?? "{}") as SwitchMiiInstructions); + } + // Parse and check all submission info const parsed = submitSchema.safeParse({ platform: formData.get("platform"), @@ -100,7 +128,7 @@ export async function POST(request: NextRequest) { gender: formData.get("gender") ?? undefined, // ZOD MOMENT miiPortraitImage: formData.get("miiPortraitImage"), - instructions: JSON.parse((formData.get("instructions") as string) ?? {}), + instructions: minifiedInstructions, qrBytesRaw: rawQrBytesRaw, @@ -156,35 +184,9 @@ export async function POST(request: NextRequest) { } // Check Mii portrait image as well (Switch) - let minifiedInstructions: Partial; if (platform === "SWITCH") { const imageValidation = await validateImage(miiPortraitImage); if (!imageValidation.valid) return rateLimit.sendResponse({ error: imageValidation.error }, imageValidation.status ?? 400); - - // Minimize instructions to save space and improve user experience - function minimize(object: Partial): Partial { - for (const key in object) { - const value = object[key as keyof SwitchMiiInstructions]; - - if (!value) { - delete object[key as keyof SwitchMiiInstructions]; - continue; - } - - // Recurse into nested objects - if (typeof value === "object") { - minimize(value as Partial); - - if (Object.keys(value).length === 0) { - delete object[key as keyof SwitchMiiInstructions]; - } - } - } - - return object; - } - - minifiedInstructions = minimize(instructions as SwitchMiiInstructions); } const qrBytes = new Uint8Array(qrBytesRaw ?? []); @@ -220,7 +222,7 @@ export async function POST(request: NextRequest) { allowedCopying: conversion.mii.allowCopying, } : { - instructions, + instructions: minifiedInstructions, }), }, }); diff --git a/src/app/mii/[id]/page.tsx b/src/app/mii/[id]/page.tsx index 8bee268..59d0011 100644 --- a/src/app/mii/[id]/page.tsx +++ b/src/app/mii/[id]/page.tsx @@ -128,7 +128,7 @@ export default async function MiiPage({ params }: Props) { alt="mii headshot" width={250} height={250} - className="drop-shadow-lg hover:scale-105 transition-transform" + className="drop-shadow-lg hover:scale-105 transition-transform w-full max-h-96 object-contain" /> {/* QR Code */} @@ -303,6 +303,9 @@ export default async function MiiPage({ params }: Props) { {mii.platform === "THREE_DS" ? : } + + {/* Instructions */} +
{JSON.stringify(mii.instructions)}
diff --git a/src/components/submit-form/index.tsx b/src/components/submit-form/index.tsx index ff598a0..fd6274a 100644 --- a/src/components/submit-form/index.tsx +++ b/src/components/submit-form/index.tsx @@ -173,15 +173,13 @@ export default function SubmitForm() { } // Convert QR code to JS (3DS) - if (platform === "THREE_DS") { - let conversion: { mii: Mii; tomodachiLifeMii: ThreeDsTomodachiLifeMii }; - try { - conversion = convertQrCode(qrBytes); - setMiiPortraitUri(conversion.mii.studioUrl({ width: 512 })); - } catch (error) { - setError(error instanceof Error ? error.message : String(error)); - return; - } + let conversion: { mii: Mii; tomodachiLifeMii: ThreeDsTomodachiLifeMii }; + try { + conversion = convertQrCode(qrBytes); + setMiiPortraitUri(conversion.mii.studioUrl({ width: 512 })); + } catch (error) { + setError(error instanceof Error ? error.message : String(error)); + return; } // Generate a new QR code for aesthetic reasons diff --git a/src/components/submit-form/mii-editor/tabs/glasses.tsx b/src/components/submit-form/mii-editor/tabs/glasses.tsx index 1086004..438ea1a 100644 --- a/src/components/submit-form/mii-editor/tabs/glasses.tsx +++ b/src/components/submit-form/mii-editor/tabs/glasses.tsx @@ -23,6 +23,7 @@ export default function GlassesTab({ instructions }: Props) {
{ diff --git a/src/lib/images.tsx b/src/lib/images.tsx index d971771..6125799 100644 --- a/src/lib/images.tsx +++ b/src/lib/images.tsx @@ -134,31 +134,38 @@ export async function generateMetadataImage(mii: Mii, author: string): Promise<{ fs.readFile(path.join(miiUploadsDirectory, "mii.webp")).then((buffer) => sharp(buffer) .png() + // extend to fix shadow bug on landscape pictures + .extend({ + left: 16, + right: 16, + background: { r: 0, g: 0, b: 0, alpha: 0 }, + }) .toBuffer() .then((pngBuffer) => `data:image/png;base64,${pngBuffer.toString("base64")}`), ), - fs.readFile(path.join(miiUploadsDirectory, "qr-code.webp")).then((buffer) => - sharp(buffer) - .png() - .toBuffer() - .then((pngBuffer) => `data:image/png;base64,${pngBuffer.toString("base64")}`), - ), + mii.platform === "THREE_DS" + ? fs.readFile(path.join(miiUploadsDirectory, "qr-code.webp")).then((buffer) => + sharp(buffer) + .png() + .toBuffer() + .then((pngBuffer) => `data:image/png;base64,${pngBuffer.toString("base64")}`), + ) + : Promise.resolve(null), loadFonts(), ]); const jsx: ReactNode = (
- {/* Mii image */} + {/* Mii portrait */}
{/* QR code */} -
- -
+ {mii.platform === "THREE_DS" ? ( +
+ +
+ ) : ( +
+ Switch Guide +

You need to manually create the Mii, visit site for instructions.

+
+ View Steps +
+
+ )}