mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-06-28 14:44:15 +00:00
feat: edit portrait and features
This commit is contained in:
parent
5ef104904a
commit
576cb698d2
3 changed files with 132 additions and 9 deletions
|
|
@ -19,6 +19,7 @@ import Dropzone from "../dropzone";
|
|||
import MiiEditor from "./mii-editor";
|
||||
import SwitchSubmitTutorialButton from "../tutorial/switch-submit";
|
||||
import { Icon } from "@iconify/react";
|
||||
import SwitchFileUpload from "./switch-file-upload";
|
||||
|
||||
interface Props {
|
||||
mii: Mii;
|
||||
|
|
@ -63,6 +64,8 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
const [tags, setTags] = useState(mii.tags);
|
||||
const [description, setDescription] = useState(mii.description);
|
||||
const [makeup, setMakeup] = useState<MiiMakeup>(mii.makeup ?? "PARTIAL");
|
||||
const [miiPortraitUri, setMiiPortraitUri] = useState<string | undefined>(`/mii/${mii.id}/image?type=mii`);
|
||||
const [miiFeaturesUri, setMiiFeaturesUri] = useState<string | undefined>(`/mii/${mii.id}/image?type=features`);
|
||||
const hasFilesChanged = useRef(false);
|
||||
|
||||
const instructions = useRef<SwitchMiiInstructions>(deepMerge(defaultInstructions, (mii.instructions as object) ?? {}));
|
||||
|
|
@ -86,6 +89,7 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
if (tags != mii.tags) formData.append("tags", JSON.stringify(tags));
|
||||
if (description && description != mii.description) formData.append("description", description);
|
||||
if (makeup != mii.makeup) formData.append("makeup", makeup);
|
||||
if (miiPortraitUri) formData.append("miiPortraitUri", miiPortraitUri);
|
||||
if (minifyInstructions(structuredClone(instructions.current)) !== (mii.instructions as object))
|
||||
formData.append("instructions", JSON.stringify(instructions.current));
|
||||
|
||||
|
|
@ -96,6 +100,32 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
});
|
||||
}
|
||||
|
||||
// Switch pictures
|
||||
async function getBlob(uri: string): Promise<Blob | null> {
|
||||
const response = await fetch(uri);
|
||||
if (!response.ok) {
|
||||
setError("Failed to get Mii portrait/features screenshot. Did you upload one?");
|
||||
return null;
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
if (!blob.type.startsWith("image/")) {
|
||||
setError("Invalid image file found");
|
||||
return null;
|
||||
}
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
if (miiPortraitUri) {
|
||||
const blob = await getBlob(miiPortraitUri);
|
||||
if (blob) formData.append("miiPortraitImage", blob);
|
||||
}
|
||||
if (miiFeaturesUri) {
|
||||
const blob = await getBlob(miiFeaturesUri);
|
||||
if (blob) formData.append("miiFeaturesImage", blob);
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/mii/${mii.id}/edit`, {
|
||||
method: "PATCH",
|
||||
body: formData,
|
||||
|
|
@ -137,7 +167,13 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
<form className="flex justify-center gap-4 w-full max-lg:flex-col max-lg:items-center">
|
||||
<div className="flex justify-center">
|
||||
<div className="w-75 h-min flex flex-col bg-zinc-50 rounded-3xl border-2 border-zinc-300 shadow-lg p-3">
|
||||
<Carousel images={[`/mii/${mii.id}/image?type=mii`, `/mii/${mii.id}/image?type=qr-code`, ...files.map((file) => URL.createObjectURL(file))]} />
|
||||
<Carousel
|
||||
images={[
|
||||
miiPortraitUri ?? `/mii/${mii.id}/image?type=mii`,
|
||||
...(mii.platform === "THREE_DS" ? [`/mii/${mii.id}/image?type=qr-code`] : [miiFeaturesUri ?? `/mii/${mii.id}/image?type=features`]),
|
||||
...files.map((file) => URL.createObjectURL(file)),
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="p-4 flex flex-col gap-1 h-full">
|
||||
<h1 className="font-bold text-2xl line-clamp-1" title={name}>
|
||||
|
|
@ -209,7 +245,7 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* Instructions (Switch only) */}
|
||||
{/* Makeup/Images/Instructions (Switch only) */}
|
||||
{mii.platform === "SWITCH" && (
|
||||
<>
|
||||
<div className="w-full grid grid-cols-3 items-start">
|
||||
|
|
@ -259,6 +295,24 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* (Switch Only) Mii Portrait */}
|
||||
<div>
|
||||
{/* Separator */}
|
||||
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium mt-8 mb-2">
|
||||
<hr className="grow border-zinc-300" />
|
||||
<span>Mii Portrait</span>
|
||||
<hr className="grow border-zinc-300" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<SwitchFileUpload text="a screenshot of your Mii here" image={miiPortraitUri} setImage={setMiiPortraitUri} forceCrop />
|
||||
<SwitchFileUpload text="a screenshot of your Mii's features here" image={miiFeaturesUri} setImage={setMiiFeaturesUri} />
|
||||
<SwitchSubmitTutorialButton />
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-zinc-400 text-center mt-2">You must upload a screenshot of the features, check tutorial on how.</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium mt-8">
|
||||
<hr className="grow border-zinc-300" />
|
||||
<span>Instructions</span>
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export default function ImageEditorPortrait({ isOpen, setIsOpen, image, setImage
|
|||
|
||||
<div className="mt-4 flex justify-center gap-2">
|
||||
<button type="button" onClick={close} className="pill button">
|
||||
Cancel
|
||||
Done
|
||||
</button>
|
||||
<button type="button" onClick={applyCrop} className="pill button">
|
||||
Crop
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue