From 8c0a7c47a35b23264a2119ea202e672985fb6cc6 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Thu, 22 May 2025 22:30:08 +0100 Subject: [PATCH] feat: share mii button --- src/app/globals.css | 4 + src/app/mii/[id]/page.tsx | 2 + src/components/share-mii-button.tsx | 185 ++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 src/components/share-mii-button.tsx diff --git a/src/app/globals.css b/src/app/globals.css index 10b381c..ffec983 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -43,6 +43,10 @@ body { @apply !bg-orange-200 outline-0 focus:ring-[3px] ring-orange-400/50 transition placeholder:text-black/40; } +.input:disabled { + @apply text-zinc-600 !bg-zinc-100 !border-zinc-300; +} + .checkbox { @apply flex items-center justify-center appearance-none size-5 bg-orange-300 border-2 border-orange-400 rounded-md cursor-pointer checked:bg-orange-400; } diff --git a/src/app/mii/[id]/page.tsx b/src/app/mii/[id]/page.tsx index e669312..c4ccd17 100644 --- a/src/app/mii/[id]/page.tsx +++ b/src/app/mii/[id]/page.tsx @@ -10,6 +10,7 @@ import { prisma } from "@/lib/prisma"; import LikeButton from "@/components/like-button"; import ImageViewer from "@/components/image-viewer"; import DeleteMiiButton from "@/components/delete-mii"; +import ShareMiiButton from "@/components/share-mii-button"; import ScanTutorialButton from "@/components/tutorial/scan"; interface Props { @@ -215,6 +216,7 @@ export default async function MiiPage({ params }: Props) { )} + Report diff --git a/src/components/share-mii-button.tsx b/src/components/share-mii-button.tsx new file mode 100644 index 0000000..081fc69 --- /dev/null +++ b/src/components/share-mii-button.tsx @@ -0,0 +1,185 @@ +"use client"; + +import Image from "next/image"; + +import { useEffect, useState } from "react"; +import { createPortal } from "react-dom"; +import { Icon } from "@iconify/react"; + +interface Props { + miiId: number; +} + +export default function ShareMiiButton({ miiId }: Props) { + const [isOpen, setIsOpen] = useState(false); + const [isVisible, setIsVisible] = useState(false); + + const [hasCopiedUrl, setHasCopiedUrl] = useState(false); + const [hasCopiedImage, setHasCopiedImage] = useState(false); + + const url = `${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}`; + + const handleCopyUrl = async () => { + await navigator.clipboard.writeText(url); + + setHasCopiedUrl(true); + + // Reset to trigger exit animation + setTimeout(() => { + setHasCopiedUrl(false); + }, 750); + }; + + const handleCopyImage = async () => { + const response = await fetch(`/mii/${miiId}/image?type=metadata`); + const blob = await response.blob(); + + await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]); + + setHasCopiedImage(true); + + // Reset to trigger exit animation + setTimeout(() => { + setHasCopiedImage(false); + }, 750); + }; + + const close = () => { + setIsVisible(false); + setTimeout(() => { + setIsOpen(false); + }, 300); + }; + + useEffect(() => { + if (isOpen) { + // slight delay to trigger animation + setTimeout(() => setIsVisible(true), 10); + } + }, [isOpen]); + + return ( + <> + + + {isOpen && + createPortal( +
+
+ +
+
+

Share Mii

+ +
+ +
+ + + {/* Copy button */} + +
+ + {/* Separator */} +
+
+ or +
+
+ +
+ mii 'metadata' image +
+ +
+
+ {/* Save button */} + + + + + {/* Copy button */} + +
+ + +
+
+
, + document.body + )} + + ); +}