From b4c2214c7a4ba44596772f52c7f0ff0041224c57 Mon Sep 17 00:00:00 2001 From: ssmithx Date: Tue, 11 Aug 2026 11:50:05 +0000 Subject: [PATCH] fix: surface NIP-07 signature wait as a distinct upload phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Episode uploads that looked 'stuck uploading to blossom' were actually stuck waiting on the browser nostr extension's own approval popup — confirmed via server/blossom logs showing zero incoming requests for the reported attempt, meaning the upload never left the browser. The UI flipped to phase 'uploading' (0%) before the NIP-07 signEvent() call resolved, so a user who didn't notice the extension's popup saw a progress bar frozen at 0% with no indication anything needed their action. Added a 'signing' phase with an explicit message telling them to check for the popup. --- frontend/src/lib/blossom.ts | 5 +++++ frontend/src/views/wizard/EpisodeWizard.vue | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/blossom.ts b/frontend/src/lib/blossom.ts index 1918d04..0345843 100644 --- a/frontend/src/lib/blossom.ts +++ b/frontend/src/lib/blossom.ts @@ -24,7 +24,12 @@ export async function uploadToBlossom( file: File, sha256: string, onProgress?: (frac: number) => void, + onSigning?: () => void, ): Promise { + // signEvent() waits on the NIP-07 extension's own approval popup, which can + // take an unbounded amount of time (or go unnoticed) — tell the caller so + // the UI doesn't say "Uploading… 0%" while nothing has actually started. + onSigning?.(); const now = Math.floor(Date.now() / 1000); const auth = await nip07().signEvent({ kind: 24242, diff --git a/frontend/src/views/wizard/EpisodeWizard.vue b/frontend/src/views/wizard/EpisodeWizard.vue index 9d45f83..4788a69 100644 --- a/frontend/src/views/wizard/EpisodeWizard.vue +++ b/frontend/src/views/wizard/EpisodeWizard.vue @@ -17,7 +17,7 @@ const podcast = ref(null); const creatingNew = ref(false); const file = ref(null); -const phase = ref<'idle' | 'hashing' | 'uploading' | 'registering'>('idle'); +const phase = ref<'idle' | 'hashing' | 'signing' | 'uploading' | 'registering'>('idle'); const progress = ref(0); const error = ref(''); @@ -31,6 +31,7 @@ const episodeUrl = ref(''); const phaseLabel = computed(() => ({ idle: '', hashing: 'Computing sha256…', + signing: 'Waiting for your nostr extension to approve the upload — check for a popup (it may be behind this window).', uploading: `Uploading to Blossom… ${(progress.value * 100).toFixed(0)}%`, registering: 'Publishing episode…', }[phase.value])); @@ -72,9 +73,17 @@ async function publish() { durationSecs.value = await probeDuration(file.value); const sha = await sha256File(file.value); - phase.value = 'uploading'; const blossomUrl = settings.public!.blossomUrl; - await uploadToBlossom(blossomUrl, file.value, sha, (f) => (progress.value = f)); + await uploadToBlossom( + blossomUrl, + file.value, + sha, + (f) => { + phase.value = 'uploading'; + progress.value = f; + }, + () => (phase.value = 'signing'), + ); uploaded.value = { sha256: sha, size: file.value.size }; phase.value = 'registering';