b39a7b800795

RecoveryWordDisplay: replace navigator.share (no-op without @capacitor/share) with a true file download via base64 data URL on native (caught by MainActivity.setDownloadListener and written to public Downloads via MediaStore); button label always reads 'download as .txt'

time
2026-05-07 02:53 UTC · 5 months ago
machine
koh2
parent
4bb8cb4b18bc
1 file src/popup/views/RecoveryWordDisplay.tsx+14 −23
src/popup/views/RecoveryWordDisplay.tsx+14 −23
@@ -127,7 +127,7 @@127127             class="peach-recovery-download-btn"128128             onClick={() => void saveRecoveryWords(words())}129129           >130-            {isNativePlatform() ? "share recovery words" : "download as .txt"}130+            download as .txt131131           </button>132132         </div>133133 @@ -192,7 +192,7 @@192192   );193193 }194194 195-async function saveRecoveryWords(words: string[]): Promise<void> {195+function saveRecoveryWords(words: string[]): void {196196   const numbered = words.map((w, i) => `${String(i + 1).padStart(2, " ")}. ${w}`).join("\n");197197   const body =198198     "PEACH RECOVERY WORDS\n" +@@ -201,30 +201,21 @@201201     "Store this file somewhere only you can access.\n\n" +202202     numbered + "\n";203203 204-  // Capacitor WebView doesn't fire <a download> — without a DownloadListener205-  // the click is a no-op. Use Web Share when available (covers Android +206-  // iOS Capacitor and modern desktop Safari), and fall back to <a download>207-  // for Chromium/Firefox on desktop where Share is unavailable or noisy.208-  if (isNativePlatform() && typeof navigator !== "undefined" && typeof navigator.share === "function") {209-    try {210-      await navigator.share({211-        title: "Peach recovery words",212-        text: body,213-      });214-      return;215-    } catch (e) {216-      // User cancelled or share failed — silently fall through to download.217-      if (e instanceof Error && /abort/i.test(e.message)) return;218-    }219-  }220-221-  const blob = new Blob([body], { type: "text/plain" });222-  const url = URL.createObjectURL(blob);223204   const a = document.createElement("a");224-  a.href = url;225205   a.download = "peach-recovery-words.txt";206+  if (isNativePlatform()) {207+    // Capacitor WebView: blob: URLs are opaque to native, but data: URLs go208+    // through MainActivity.setDownloadListener which writes them to public209+    // Downloads via MediaStore. Encode as base64 to keep the data URL210+    // intact through the WebView's URL plumbing.211+    const b64 = btoa(unescape(encodeURIComponent(body)));212+    a.href = `data:text/plain;base64,${b64}`;213+  } else {214+    const blob = new Blob([body], { type: "text/plain" });215+    a.href = URL.createObjectURL(blob);216+    setTimeout(() => URL.revokeObjectURL(a.href), 1000);217+  }226218   document.body.appendChild(a);227219   a.click();228220   a.remove();229-  URL.revokeObjectURL(url);230221 }\ No newline at end of file

view file