diff --git a/src/components/WalletModal.tsx b/src/components/WalletModal.tsx
index 05a3d02..e6725f8 100644
--- a/src/components/WalletModal.tsx
+++ b/src/components/WalletModal.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { Wallet, Plus, Trash2, Zap, Globe, WalletMinimal, CheckCircle } from 'lucide-react';
+import { Wallet, Plus, Trash2, Zap, Globe, WalletMinimal, CheckCircle, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Dialog,
@@ -10,6 +10,15 @@ import {
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
+import {
+ Drawer,
+ DrawerContent,
+ DrawerDescription,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+ DrawerClose,
+} from '@/components/ui/drawer';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
@@ -18,6 +27,7 @@ import { Separator } from '@/components/ui/separator';
import { useNWC } from '@/hooks/useNWCContext';
import { useWallet } from '@/hooks/useWallet';
import { useToast } from '@/hooks/useToast';
+import { useIsMobile } from '@/hooks/useIsMobile';
interface WalletModalProps {
children?: React.ReactNode;
@@ -30,6 +40,7 @@ export function WalletModal({ children, className }: WalletModalProps) {
const [connectionUri, setConnectionUri] = useState('');
const [alias, setAlias] = useState('');
const [isConnecting, setIsConnecting] = useState(false);
+ const isMobile = useIsMobile();
const {
connections,
@@ -81,6 +92,213 @@ export function WalletModal({ children, className }: WalletModalProps) {
});
};
+ // Shared content component
+ const WalletContent = () => (
+
+ {/* Current Status */}
+
+
Current Status
+
+
+ {/* WebLN */}
+
+
+
+
+
WebLN
+
Browser extension
+
+
+
+ {hasWebLN && }
+
+ {isDetecting ? "..." : hasWebLN ? "Ready" : "Not Found"}
+
+
+
+
+ {/* NWC */}
+
+
+
+
+
Nostr Wallet Connect
+
+ {connections.length > 0
+ ? `${connections.length} wallet${connections.length !== 1 ? 's' : ''} connected`
+ : "Remote wallet connection"
+ }
+
+
+
+
+ {hasNWC && }
+
+ {hasNWC ? "Ready" : "None"}
+
+
+
+
+
+
+
+
+ {/* NWC Management */}
+
+
+
Nostr Wallet Connect
+
+
+
+
+ Add
+
+
+
+
+ Connect NWC Wallet
+
+ Enter your connection string from a compatible wallet.
+
+
+
+
+ Wallet Name (optional)
+ setAlias(e.target.value)}
+ />
+
+
+ Connection URI
+
+
+
+
+ {isConnecting ? 'Connecting...' : 'Connect'}
+
+
+
+
+
+ {/* Connected Wallets List */}
+ {connections.length === 0 ? (
+
+ ) : (
+
+ {connections.map((connection) => {
+ const info = connectionInfo[connection.connectionString];
+ const isActive = activeConnection === connection.connectionString;
+
+ return (
+
+
+
+
+
+ {connection.alias || info?.alias || 'Lightning Wallet'}
+
+
+ NWC Connection
+
+
+
+
+ {isActive && }
+ {!isActive && (
+ handleSetActive(connection.connectionString)}
+ >
+
+
+ )}
+ handleRemoveConnection(connection.connectionString)}
+ >
+
+
+
+
+ );
+ })}
+
+ )}
+
+
+ {/* Help */}
+ {!hasWebLN && connections.length === 0 && (
+ <>
+
+
+
+ Install a WebLN extension or connect a NWC wallet for zaps.
+
+
+ >
+ )}
+
+ );
+
+ if (isMobile) {
+ return (
+
+
+ {children || (
+
+
+ Wallet Settings
+
+ )}
+
+
+
+ {/* Close button */}
+
+
+
+ Close
+
+
+
+
+
+ Lightning Wallet
+
+
+ Connect your lightning wallet to send zaps instantly.
+
+
+
+
+
+
+
+ );
+ }
+
return (
@@ -101,168 +319,7 @@ export function WalletModal({ children, className }: WalletModalProps) {
Connect your lightning wallet to send zaps instantly.
-
-
- {/* Current Status */}
-
-
Current Status
-
-
- {/* WebLN */}
-
-
-
-
-
WebLN
-
Browser extension
-
-
-
- {hasWebLN && }
-
- {isDetecting ? "..." : hasWebLN ? "Ready" : "Not Found"}
-
-
-
-
- {/* NWC */}
-
-
-
-
-
Nostr Wallet Connect
-
- {connections.length > 0
- ? `${connections.length} wallet${connections.length !== 1 ? 's' : ''} connected`
- : "Remote wallet connection"
- }
-
-
-
-
- {hasNWC && }
-
- {hasNWC ? "Ready" : "None"}
-
-
-
-
-
-
-
-
- {/* NWC Management */}
-
-
-
Nostr Wallet Connect
-
-
-
-
- Add
-
-
-
-
- Connect NWC Wallet
-
- Enter your connection string from a compatible wallet.
-
-
-
-
- Wallet Name (optional)
- setAlias(e.target.value)}
- />
-
-
- Connection URI
-
-
-
-
- {isConnecting ? 'Connecting...' : 'Connect'}
-
-
-
-
-
- {/* Connected Wallets List */}
- {connections.length === 0 ? (
-
- ) : (
-
- {connections.map((connection) => {
- const info = connectionInfo[connection.connectionString];
- const isActive = activeConnection === connection.connectionString;
-
- return (
-
-
-
-
-
- {connection.alias || info?.alias || 'Lightning Wallet'}
-
-
- NWC Connection
-
-
-
-
- {isActive && }
- {!isActive && (
- handleSetActive(connection.connectionString)}
- >
-
-
- )}
- handleRemoveConnection(connection.connectionString)}
- >
-
-
-
-
- );
- })}
-
- )}
-
-
- {/* Help */}
- {!hasWebLN && connections.length === 0 && (
- <>
-
-
-
- Install a WebLN extension or connect a NWC wallet for zaps.
-
-
- >
- )}
-
+
);
diff --git a/src/components/ZapDialog.tsx b/src/components/ZapDialog.tsx
index ebc63aa..bbf9206 100644
--- a/src/components/ZapDialog.tsx
+++ b/src/components/ZapDialog.tsx
@@ -1,15 +1,23 @@
import { useState, useEffect, useRef } from 'react';
-import { Zap, Copy, Check, ExternalLink, Sparkle, Sparkles, Star, Rocket } from 'lucide-react';
+import { Zap, Copy, Check, ExternalLink, Sparkle, Sparkles, Star, Rocket, ArrowLeft, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
- DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
+import {
+ Drawer,
+ DrawerContent,
+ DrawerDescription,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+ DrawerClose,
+} from '@/components/ui/drawer';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
@@ -21,6 +29,7 @@ import { useAuthor } from '@/hooks/useAuthor';
import { useToast } from '@/hooks/useToast';
import { useZaps } from '@/hooks/useZaps';
import { useWallet } from '@/hooks/useWallet';
+import { useIsMobile } from '@/hooks/useIsMobile';
import type { Event } from 'nostr-tools';
import QRCode from 'qrcode';
@@ -50,6 +59,7 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
const [copied, setCopied] = useState(false);
const [qrCodeUrl, setQrCodeUrl] = useState('');
const inputRef = useRef(null);
+ const isMobile = useIsMobile();
useEffect(() => {
if (target) {
@@ -120,10 +130,321 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
zap(finalAmount, comment);
};
+ // Shared content component
+ const ZapContent = () => (
+ <>
+ {invoice ? (
+
+ {/* Payment amount display */}
+
+
+
+
+ {/* QR Code */}
+
+
+
+ {qrCodeUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {/* Invoice input */}
+
+
Lightning Invoice
+
+ e.currentTarget.select()}
+ />
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {/* Payment buttons */}
+
+ {hasWebLN && (
+
{
+ const finalAmount = typeof amount === 'string' ? parseInt(amount, 10) : amount;
+ zap(finalAmount, comment);
+ }}
+ disabled={isZapping}
+ className="w-full"
+ size="lg"
+ >
+
+ {isZapping ? "Processing..." : "Pay with WebLN"}
+
+ )}
+
+
+
+ Open in Lightning Wallet
+
+
+
+ Scan the QR code or copy the invoice to pay with any Lightning wallet.
+
+
+
+ ) : (
+ <>
+
+
{
+ if (value) {
+ setAmount(parseInt(value, 10));
+ }
+ }}
+ className="grid grid-cols-5 gap-2 w-full"
+ >
+ {presetAmounts.map(({ amount: presetAmount, icon: Icon }) => (
+
+
+ {presetAmount}
+
+ ))}
+
+
+
setAmount(e.target.value)}
+ className="w-full"
+ />
+
+
+
+ {isZapping ? (
+ 'Creating invoice...'
+ ) : (
+ <>
+
+ Zap {amount} sats
+ >
+ )}
+
+
+ >
+ )}
+ >
+ );
+
if (!user || user.pubkey === target.pubkey || !author?.metadata?.lud06 && !author?.metadata?.lud16) {
return null;
}
+ if (isMobile) {
+ // Full screen payment view on mobile
+ if (invoice) {
+ return (
+
+ {/* Header with back and close buttons */}
+
+
{
+ setInvoice(null);
+ setQrCodeUrl('');
+ }}
+ className="flex items-center gap-2"
+ >
+
+
+
+
Lightning Payment
+
+
setOpen(false)}
+ >
+
+ Close
+
+
+
+ {/* Content */}
+
+
+ {/* Payment amount display */}
+
+
{amount} sats
+
Lightning Network Payment
+
+
+
+
+ {/* QR Code */}
+
+
+
+ {qrCodeUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {/* Invoice input */}
+
+
Lightning Invoice
+
+ e.currentTarget.select()}
+ />
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {/* Payment buttons */}
+
+ {hasWebLN && (
+
{
+ const finalAmount = typeof amount === 'string' ? parseInt(amount, 10) : amount;
+ zap(finalAmount, comment);
+ }}
+ disabled={isZapping}
+ className="w-full"
+ size="lg"
+ >
+
+ {isZapping ? "Processing..." : "Pay with WebLN"}
+
+ )}
+
+
+
+ Open in Lightning Wallet
+
+
+
+ Scan the QR code or copy the invoice to pay with any Lightning wallet.
+
+
+
+
+
+ );
+ }
+
+ // Drawer for amount selection on mobile
+ return (
+
+
+
+ {children}
+
+
+
+
+ {/* Close button */}
+
+
+
+ Close
+
+
+
+
+ Send a Zap
+
+
+ Zaps are small Bitcoin payments that support the creator of this item.
+ {' '}If you enjoyed this, consider sending a zap!
+
+
+
+
+
+
+
+ );
+ }
+
return (
@@ -131,12 +452,14 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
{children}
-
+
- {invoice ? 'Lightning Payment' : 'Send a Zap'}
-
+
+ {invoice ? 'Lightning Payment' : 'Send a Zap'}
+
+
{invoice ? (
- 'Scan the QR code or copy the invoice to pay with any Lightning wallet'
+ 'Pay with Bitcoin Lightning Network'
) : (
<>
Zaps are small Bitcoin payments that support the creator of this item.
@@ -145,149 +468,9 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
)}
- {invoice ? (
-
- {/* Payment amount display */}
-
-
{amount} sats
-
Lightning Network Payment
-
-
-
-
- {/* QR Code */}
-
-
-
- {qrCodeUrl ? (
-
- ) : (
-
- )}
-
-
-
-
- {/* Invoice input */}
-
-
Lightning Invoice
-
- e.currentTarget.select()}
- />
-
- {copied ? (
-
- ) : (
-
- )}
-
-
-
-
- {/* Payment buttons */}
-
- {hasWebLN && (
-
{
- const finalAmount = typeof amount === 'string' ? parseInt(amount, 10) : amount;
- zap(finalAmount, comment);
- }}
- disabled={isZapping}
- className="w-full"
- size="lg"
- >
-
- {isZapping ? "Processing..." : "Pay with WebLN"}
-
- )}
-
-
-
- Open in Lightning Wallet
-
-
-
- Scan the QR code or copy the invoice to pay with any Lightning wallet
-
-
-
- ) : (
- <>
-
-
{
- if (value) {
- setAmount(parseInt(value, 10));
- }
- }}
- className="grid grid-cols-5 gap-2"
- >
- {presetAmounts.map(({ amount: presetAmount, icon: Icon }) => (
-
-
- {presetAmount}
-
- ))}
-
-
-
setAmount(e.target.value)}
- />
-
-
-
- {isZapping ? (
- 'Creating invoice...'
- ) : (
- <>
-
- Zap {amount} sats
- >
- )}
-
-
- >
- )}
+
+
+
);