diff --git a/src/components/InitialSyncGate.tsx b/src/components/InitialSyncGate.tsx index e54471f0..e09cc9ad 100644 --- a/src/components/InitialSyncGate.tsx +++ b/src/components/InitialSyncGate.tsx @@ -1,3 +1,4 @@ +import { Capacitor } from "@capacitor/core"; import type { NostrEvent, NostrMetadata } from "@nostrify/nostrify"; import { useNostr } from "@nostrify/react"; import { useQueryClient } from "@tanstack/react-query"; @@ -279,6 +280,11 @@ function SetupQuestionnaire({ await downloadTextFile(filename, nsec); + // Let the user know where the file ended up on Android + if (Capacitor.getPlatform() === "android") { + toast({ title: "Key saved", description: `Saved to Documents/${filename}` }); + } + // Log in with the new key login.nsec(nsec); next(); diff --git a/src/components/auth/SignupDialog.tsx b/src/components/auth/SignupDialog.tsx index b732049c..19a82d43 100644 --- a/src/components/auth/SignupDialog.tsx +++ b/src/components/auth/SignupDialog.tsx @@ -1,6 +1,7 @@ // NOTE: This file is stable and usually should not be modified. // It is important that all functionality in this file is preserved, and should only be modified if explicitly requested. +import { Capacitor } from '@capacitor/core'; import React, { useState, useEffect, useRef } from 'react'; import { Download, Eye, EyeOff, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; @@ -58,6 +59,10 @@ const SignupDialog: React.FC = ({ isOpen, onClose }) => { await downloadTextFile(filename, nsec); + if (Capacitor.getPlatform() === 'android') { + toast({ title: 'Key saved', description: `Saved to Documents/${filename}` }); + } + // Continue to profile step login.nsec(nsec); setStep('profile'); diff --git a/src/lib/downloadFile.ts b/src/lib/downloadFile.ts index 3b424ee1..c5b12779 100644 --- a/src/lib/downloadFile.ts +++ b/src/lib/downloadFile.ts @@ -4,27 +4,41 @@ import { Capacitor } from '@capacitor/core'; * Download a text file to the user's device. * * On the web this uses the classic `` trick. - * On native (Capacitor iOS/Android) this writes to a temp file via - * the Filesystem plugin and presents the native share sheet so the - * user can save / AirDrop / share the file. + * On Android it writes directly to the app-scoped Documents directory + * (no extra permissions needed on Android 11+). + * On iOS it writes to a temp file and presents the native share sheet. */ export async function downloadTextFile(filename: string, content: string): Promise { - if (Capacitor.isNativePlatform()) { + const platform = Capacitor.getPlatform(); + + if (platform === 'android') { + const { Filesystem, Directory } = await import('@capacitor/filesystem'); + + // Write directly to the Documents directory. On Android 11+ the app can + // write to its own scoped area inside public Documents without needing + // WRITE_EXTERNAL_STORAGE permission. + await Filesystem.writeFile({ + path: filename, + data: content, + directory: Directory.Documents, + }); + } else if (platform === 'ios') { const { Filesystem, Directory } = await import('@capacitor/filesystem'); const { Share } = await import('@capacitor/share'); - // Write to the cache directory (always writable, no permissions needed) const result = await Filesystem.writeFile({ path: filename, data: content, directory: Directory.Cache, }); - // Present the native share sheet so the user can save / share the file - await Share.share({ - title: filename, - url: result.uri, - }); + // On iOS there is no user-visible Downloads folder, so present the + // share sheet and let the user choose where to save / send the file. + try { + await Share.share({ title: filename, url: result.uri }); + } catch { + // User dismissed the share sheet — not a real failure + } } else { // Web: use the anchor-click download pattern const blob = new Blob([content], { type: 'text/plain; charset=utf-8' });