1febc9d6daf1

RecoveryWordDisplay: route save through navigator.share on native (Capacitor WebView ignores <a download>); button label flips to 'share recovery words' on mobile; desktop keeps <a download> fallback

time
2026-05-07 02:27 UTC · 5 months ago
machine
koh2
parent
2ce7d020ae13
1 file src/popup/views/RecoveryWordDisplay.tsx+22 −3
src/popup/views/RecoveryWordDisplay.tsx+22 −3
@@ -2,6 +2,7 @@22 import { sendMessage } from "../App";33 import { PeachWordmark } from "../components/PeachMark";44 import { RecoveryRuneBackground } from "../components/RecoveryRuneBackground";5+import { isNativePlatform } from "../../shared/native";56 67 interface RecoveryWordDisplayViewProps {78   onDone: () => void;@@ -124,9 +125,9 @@124125           <button125126             type="button"126127             class="peach-recovery-download-btn"127-            onClick={() => downloadRecoveryWords(words())}128+            onClick={() => void saveRecoveryWords(words())}128129           >129-            download as .txt130+            {isNativePlatform() ? "share recovery words" : "download as .txt"}130131           </button>131132         </div>132133 @@ -191,7 +192,7 @@191192   );192193 }193194 194-function downloadRecoveryWords(words: string[]): void {195+async function saveRecoveryWords(words: string[]): Promise<void> {195196   const numbered = words.map((w, i) => `${String(i + 1).padStart(2, " ")}. ${w}`).join("\n");196197   const body =197198     "PEACH RECOVERY WORDS\n" +@@ -199,6 +200,24 @@199200     "Anyone with these 12 words can recover your vault.\n" +200201     "Store this file somewhere only you can access.\n\n" +201202     numbered + "\n";203+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+202221   const blob = new Blob([body], { type: "text/plain" });203222   const url = URL.createObjectURL(blob);204223   const a = document.createElement("a");

view file