From c5861031d02f01abd3ef58100eeea0ee06c22fb5 Mon Sep 17 00:00:00 2001 From: zurdi Date: Mon, 22 Dec 2025 17:25:26 +0000 Subject: [PATCH] feat: enhance drop zone functionality and improve error handling in Patcher.vue --- frontend/src/views/Patcher.vue | 79 ++++++++++++---------------------- 1 file changed, 28 insertions(+), 51 deletions(-) diff --git a/frontend/src/views/Patcher.vue b/frontend/src/views/Patcher.vue index 3ebbb9fc1..1606dbbe9 100644 --- a/frontend/src/views/Patcher.vue +++ b/frontend/src/views/Patcher.vue @@ -6,7 +6,6 @@ import { inject, ref, onMounted } from "vue"; import { useI18n } from "vue-i18n"; import MissingFromFSIcon from "@/components/common/MissingFromFSIcon.vue"; import PlatformIcon from "@/components/common/Platform/PlatformIcon.vue"; -import platformApi from "@/services/api/platform"; import romApi from "@/services/api/rom"; import socket from "@/services/socket"; import storeHeartbeat from "@/stores/heartbeat"; @@ -19,33 +18,25 @@ import { formatBytes } from "@/utils"; const { t } = useI18n(); const platformsStore = storePlatforms(); const { filteredPlatforms } = storeToRefs(platformsStore); - // Declare globals provided by local scripts declare const BinFile: any; declare const RomPatcher: any; - const loadError = ref(null); const coreLoaded = ref(false); - const romFile = ref(null); const patchFile = ref(null); - const romBin = ref(null); const patchBin = ref(null); - const romDropZoneRef = ref(null); const patchDropZoneRef = ref(null); const romInputRef = ref(null); const patchInputRef = ref(null); - const applying = ref(false); const saveIntoRomM = ref(false); const selectedPlatform = ref(null); - const emitter = inject>("emitter"); const heartbeat = storeHeartbeat(); const scanningStore = storeScanning(); - // Load core scripts via absolute asset paths (mirrors emulator loader approach) const PATCHER_BASE_PATH = "/assets/patcherjs"; const CORE_SCRIPTS = [ @@ -63,7 +54,6 @@ const CORE_SCRIPTS = [ `${PATCHER_BASE_PATH}/modules/RomPatcher.format.vcdiff.js`, `${PATCHER_BASE_PATH}/RomPatcher.js`, ]; - const supportedPatchFormats = [ ".ips", ".ups", @@ -75,6 +65,16 @@ const supportedPatchFormats = [ ".pmsr", ".vcdiff", ]; +const { isOverDropZone: isOverRomDropZone } = useDropZone(romDropZoneRef, { + onDrop: onRomDrop, + multiple: false, + preventDefaultForUnhandled: true, +}); +const { isOverDropZone: isOverPatchDropZone } = useDropZone(patchDropZoneRef, { + onDrop: onPatchDrop, + multiple: false, + preventDefaultForUnhandled: true, +}); function loadScriptSequentially(urls: string[]): Promise { return new Promise((resolve, reject) => { @@ -224,38 +224,14 @@ async function patchRom() { } async function uploadPatchedRom(patchedBin: any) { - if (!selectedPlatform.value) return; - - // Create a platform if needed - if (selectedPlatform.value.id === -1) { - await platformApi - .uploadPlatform({ fsSlug: selectedPlatform.value.fs_slug }) - .then(({ data }) => { - emitter?.emit("snackbarShow", { - msg: `Platform ${selectedPlatform.value?.name} created successfully!`, - icon: "mdi-check-bold", - color: "green", - timeout: 2000, - }); - selectedPlatform.value = data; - }) - .catch((error) => { - console.error(error); - emitter?.emit("snackbarShow", { - msg: error.response?.data?.detail || "Failed to create platform", - icon: "mdi-close-circle", - color: "red", - }); - throw error; - }); + if (!selectedPlatform.value) { + throw new Error("No platform selected."); } - const platformId = selectedPlatform.value.id; // Convert the patched BinFile to a File object // Try to get binary data from various possible properties let binaryData: Uint8Array | ArrayBuffer | null = null; - if (patchedBin._u8array instanceof Uint8Array) { binaryData = patchedBin._u8array; } else if (patchedBin.u8array instanceof Uint8Array) { @@ -277,7 +253,13 @@ async function uploadPatchedRom(patchedBin: any) { throw new Error("Unable to extract binary data from patched ROM"); } - const blob = new Blob([binaryData], { type: "application/octet-stream" }); + // Ensure binaryData is a proper Uint8Array with ArrayBuffer backing + const uint8array = + binaryData instanceof Uint8Array + ? new Uint8Array(binaryData) + : new Uint8Array(binaryData); + + const blob = new Blob([uint8array], { type: "application/octet-stream" }); const fileName = patchedBin.fileName || patchedBin.name || "patched_rom"; const file = new File([blob], fileName, { type: "application/octet-stream" }); @@ -294,7 +276,14 @@ async function uploadPatchedRom(patchedBin: any) { const failedUploads = responses.filter((d) => d.status === "rejected"); if (successfulUploads.length === 0) { - throw new Error("Failed to upload patched ROM"); + // Get detailed error message from the first failed upload + const firstFailure = failedUploads[0] as PromiseRejectedResult; + const errorDetail = + firstFailure?.reason?.response?.data?.detail || + firstFailure?.reason?.message || + "Upload failed with unknown error"; + console.error("Upload failed:", firstFailure); + throw new Error(errorDetail); } emitter?.emit("snackbarShow", { @@ -330,18 +319,6 @@ onMounted(async () => { // Preload core for faster interaction await ensureCoreLoaded(); }); - -const { isOverDropZone: isOverRomDropZone } = useDropZone(romDropZoneRef, { - onDrop: onRomDrop, - multiple: false, - preventDefaultForUnhandled: true, -}); - -const { isOverDropZone: isOverPatchDropZone } = useDropZone(patchDropZoneRef, { - onDrop: onPatchDrop, - multiple: false, - preventDefaultForUnhandled: true, -});