fix redraw issue for wallet configuration
This commit is contained in:
parent
6845e6cf7c
commit
97a493c218
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, forwardRef } from 'react';
|
||||||
import { Wallet, Plus, Trash2, Zap, Globe, WalletMinimal, CheckCircle, X } from 'lucide-react';
|
import { Wallet, Plus, Trash2, Zap, Globe, WalletMinimal, CheckCircle, X } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@ -28,77 +28,70 @@ import { useNWC } from '@/hooks/useNWCContext';
|
|||||||
import { useWallet } from '@/hooks/useWallet';
|
import { useWallet } from '@/hooks/useWallet';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||||
|
import type { NWCConnection, NWCInfo } from '@/hooks/useNWC';
|
||||||
|
|
||||||
interface WalletModalProps {
|
interface WalletModalProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WalletModal({ children, className }: WalletModalProps) {
|
// Extracted AddWalletContent to prevent re-renders
|
||||||
const [open, setOpen] = useState(false);
|
const AddWalletContent = forwardRef<HTMLDivElement, {
|
||||||
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
alias: string;
|
||||||
const [connectionUri, setConnectionUri] = useState('');
|
setAlias: (value: string) => void;
|
||||||
const [alias, setAlias] = useState('');
|
connectionUri: string;
|
||||||
const [isConnecting, setIsConnecting] = useState(false);
|
setConnectionUri: (value: string) => void;
|
||||||
const isMobile = useIsMobile();
|
}>(({ alias, setAlias, connectionUri, setConnectionUri }, ref) => (
|
||||||
|
<div className="space-y-4 px-4" ref={ref}>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="alias">Wallet Name (optional)</Label>
|
||||||
|
<Input
|
||||||
|
id="alias"
|
||||||
|
placeholder="My Lightning Wallet"
|
||||||
|
value={alias}
|
||||||
|
onChange={(e) => setAlias(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="connection-uri">Connection URI</Label>
|
||||||
|
<Textarea
|
||||||
|
id="connection-uri"
|
||||||
|
placeholder="nostr+walletconnect://..."
|
||||||
|
value={connectionUri}
|
||||||
|
onChange={(e) => setConnectionUri(e.target.value)}
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
AddWalletContent.displayName = 'AddWalletContent';
|
||||||
|
|
||||||
const {
|
// Extracted WalletContent to prevent re-renders
|
||||||
|
const WalletContent = forwardRef<HTMLDivElement, {
|
||||||
|
hasWebLN: boolean;
|
||||||
|
isDetecting: boolean;
|
||||||
|
hasNWC: boolean;
|
||||||
|
connections: NWCConnection[];
|
||||||
|
connectionInfo: Record<string, NWCInfo>;
|
||||||
|
activeConnection: string | null;
|
||||||
|
handleSetActive: (cs: string) => void;
|
||||||
|
handleRemoveConnection: (cs: string) => void;
|
||||||
|
setAddDialogOpen: (open: boolean) => void;
|
||||||
|
}>(({
|
||||||
|
hasWebLN,
|
||||||
|
isDetecting,
|
||||||
|
hasNWC,
|
||||||
connections,
|
connections,
|
||||||
activeConnection,
|
|
||||||
connectionInfo,
|
connectionInfo,
|
||||||
addConnection,
|
activeConnection,
|
||||||
removeConnection,
|
handleSetActive,
|
||||||
setActiveConnection
|
handleRemoveConnection,
|
||||||
} = useNWC();
|
setAddDialogOpen
|
||||||
|
}, ref) => (
|
||||||
const { hasWebLN, isDetecting } = useWallet();
|
<div className="space-y-6 px-4 pb-4" ref={ref}>
|
||||||
|
|
||||||
// Calculate hasNWC directly from connections to ensure reactivity
|
|
||||||
const hasNWC = connections.length > 0 && connections.some(c => c.isConnected);
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const handleAddConnection = async () => {
|
|
||||||
if (!connectionUri.trim()) {
|
|
||||||
toast({
|
|
||||||
title: 'Connection URI required',
|
|
||||||
description: 'Please enter a valid NWC connection URI.',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsConnecting(true);
|
|
||||||
try {
|
|
||||||
const success = await addConnection(connectionUri.trim(), alias.trim() || undefined);
|
|
||||||
if (success) {
|
|
||||||
setConnectionUri('');
|
|
||||||
setAlias('');
|
|
||||||
setAddDialogOpen(false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
setIsConnecting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveConnection = (connectionString: string) => {
|
|
||||||
removeConnection(connectionString);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSetActive = (connectionString: string) => {
|
|
||||||
setActiveConnection(connectionString);
|
|
||||||
toast({
|
|
||||||
title: 'Active wallet changed',
|
|
||||||
description: 'The selected wallet is now active for zaps.',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Shared content component
|
|
||||||
const WalletContent = () => (
|
|
||||||
<div className="space-y-6 px-4 pb-4">
|
|
||||||
{/* Current Status */}
|
{/* Current Status */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h3 className="font-medium">Current Status</h3>
|
<h3 className="font-medium">Current Status</h3>
|
||||||
|
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
{/* WebLN */}
|
{/* WebLN */}
|
||||||
<div className="flex items-center justify-between p-3 border rounded-lg">
|
<div className="flex items-center justify-between p-3 border rounded-lg">
|
||||||
@ -116,7 +109,6 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* NWC */}
|
{/* NWC */}
|
||||||
<div className="flex items-center justify-between p-3 border rounded-lg">
|
<div className="flex items-center justify-between p-3 border rounded-lg">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@ -140,59 +132,15 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
{/* NWC Management */}
|
{/* NWC Management */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="font-medium">Nostr Wallet Connect</h3>
|
<h3 className="font-medium">Nostr Wallet Connect</h3>
|
||||||
<Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>
|
<Button size="sm" variant="outline" onClick={() => setAddDialogOpen(true)}>
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button size="sm" variant="outline">
|
|
||||||
<Plus className="h-4 w-4 mr-1" />
|
<Plus className="h-4 w-4 mr-1" />
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Connect NWC Wallet</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Enter your connection string from a compatible wallet.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="space-y-4 px-4">
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="alias">Wallet Name (optional)</Label>
|
|
||||||
<Input
|
|
||||||
id="alias"
|
|
||||||
placeholder="My Lightning Wallet"
|
|
||||||
value={alias}
|
|
||||||
onChange={(e) => setAlias(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="connection-uri">Connection URI</Label>
|
|
||||||
<Textarea
|
|
||||||
id="connection-uri"
|
|
||||||
placeholder="nostr+walletconnect://..."
|
|
||||||
value={connectionUri}
|
|
||||||
onChange={(e) => setConnectionUri(e.target.value)}
|
|
||||||
rows={3}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter className="px-4">
|
|
||||||
<Button
|
|
||||||
onClick={handleAddConnection}
|
|
||||||
disabled={isConnecting || !connectionUri.trim()}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{isConnecting ? 'Connecting...' : 'Connect'}
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
{/* Connected Wallets List */}
|
{/* Connected Wallets List */}
|
||||||
{connections.length === 0 ? (
|
{connections.length === 0 ? (
|
||||||
@ -204,7 +152,6 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
{connections.map((connection) => {
|
{connections.map((connection) => {
|
||||||
const info = connectionInfo[connection.connectionString];
|
const info = connectionInfo[connection.connectionString];
|
||||||
const isActive = activeConnection === connection.connectionString;
|
const isActive = activeConnection === connection.connectionString;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={connection.connectionString} className={`flex items-center justify-between p-3 border rounded-lg ${isActive ? 'ring-2 ring-primary' : ''}`}>
|
<div key={connection.connectionString} className={`flex items-center justify-between p-3 border rounded-lg ${isActive ? 'ring-2 ring-primary' : ''}`}>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@ -243,7 +190,6 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Help */}
|
{/* Help */}
|
||||||
{!hasWebLN && connections.length === 0 && (
|
{!hasWebLN && connections.length === 0 && (
|
||||||
<>
|
<>
|
||||||
@ -256,10 +202,109 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
));
|
||||||
|
WalletContent.displayName = 'WalletContent';
|
||||||
|
|
||||||
|
export function WalletModal({ children, className }: WalletModalProps) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
||||||
|
const [connectionUri, setConnectionUri] = useState('');
|
||||||
|
const [alias, setAlias] = useState('');
|
||||||
|
const [isConnecting, setIsConnecting] = useState(false);
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
const {
|
||||||
|
connections,
|
||||||
|
activeConnection,
|
||||||
|
connectionInfo,
|
||||||
|
addConnection,
|
||||||
|
removeConnection,
|
||||||
|
setActiveConnection
|
||||||
|
} = useNWC();
|
||||||
|
|
||||||
|
const { hasWebLN, isDetecting } = useWallet();
|
||||||
|
|
||||||
|
const hasNWC = connections.length > 0 && connections.some(c => c.isConnected);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const handleAddConnection = async () => {
|
||||||
|
if (!connectionUri.trim()) {
|
||||||
|
toast({
|
||||||
|
title: 'Connection URI required',
|
||||||
|
description: 'Please enter a valid NWC connection URI.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsConnecting(true);
|
||||||
|
try {
|
||||||
|
const success = await addConnection(connectionUri.trim(), alias.trim() || undefined);
|
||||||
|
if (success) {
|
||||||
|
setConnectionUri('');
|
||||||
|
setAlias('');
|
||||||
|
setAddDialogOpen(false);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsConnecting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveConnection = (connectionString: string) => {
|
||||||
|
removeConnection(connectionString);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSetActive = (connectionString: string) => {
|
||||||
|
setActiveConnection(connectionString);
|
||||||
|
toast({
|
||||||
|
title: 'Active wallet changed',
|
||||||
|
description: 'The selected wallet is now active for zaps.',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const walletContentProps = {
|
||||||
|
hasWebLN,
|
||||||
|
isDetecting,
|
||||||
|
hasNWC,
|
||||||
|
connections,
|
||||||
|
connectionInfo,
|
||||||
|
activeConnection,
|
||||||
|
handleSetActive,
|
||||||
|
handleRemoveConnection,
|
||||||
|
setAddDialogOpen,
|
||||||
|
};
|
||||||
|
|
||||||
|
const addWalletDialog = (
|
||||||
|
<Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Connect NWC Wallet</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Enter your connection string from a compatible wallet.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<AddWalletContent
|
||||||
|
alias={alias}
|
||||||
|
setAlias={setAlias}
|
||||||
|
connectionUri={connectionUri}
|
||||||
|
setConnectionUri={setConnectionUri}
|
||||||
|
/>
|
||||||
|
<DialogFooter className="px-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleAddConnection}
|
||||||
|
disabled={isConnecting || !connectionUri.trim()}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{isConnecting ? 'Connecting...' : 'Connect'}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Drawer open={open} onOpenChange={setOpen}>
|
<Drawer open={open} onOpenChange={setOpen}>
|
||||||
<DrawerTrigger asChild>
|
<DrawerTrigger asChild>
|
||||||
{children || (
|
{children || (
|
||||||
@ -271,18 +316,12 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</DrawerTrigger>
|
</DrawerTrigger>
|
||||||
<DrawerContent className="h-full">
|
<DrawerContent className="h-full">
|
||||||
<DrawerHeader className="text-center relative">
|
<DrawerHeader className="text-center relative">
|
||||||
{/* Close button */}
|
|
||||||
<DrawerClose asChild>
|
<DrawerClose asChild>
|
||||||
<Button
|
<Button variant="ghost" size="sm" className="absolute right-4 top-4">
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="absolute right-4 top-4"
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DrawerClose>
|
</DrawerClose>
|
||||||
|
|
||||||
<DrawerTitle className="flex items-center justify-center gap-2 pt-2">
|
<DrawerTitle className="flex items-center justify-center gap-2 pt-2">
|
||||||
<Wallet className="h-5 w-5" />
|
<Wallet className="h-5 w-5" />
|
||||||
Lightning Wallet
|
Lightning Wallet
|
||||||
@ -292,14 +331,42 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
</DrawerDescription>
|
</DrawerDescription>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
<div className="overflow-y-auto">
|
<div className="overflow-y-auto">
|
||||||
<WalletContent />
|
<WalletContent {...walletContentProps} />
|
||||||
</div>
|
</div>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
{/* Render Add Wallet as a separate Drawer for mobile */}
|
||||||
|
<Drawer open={addDialogOpen} onOpenChange={setAddDialogOpen}>
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerHeader>
|
||||||
|
<DrawerTitle>Connect NWC Wallet</DrawerTitle>
|
||||||
|
<DrawerDescription>
|
||||||
|
Enter your connection string from a compatible wallet.
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
<AddWalletContent
|
||||||
|
alias={alias}
|
||||||
|
setAlias={setAlias}
|
||||||
|
connectionUri={connectionUri}
|
||||||
|
setConnectionUri={setConnectionUri}
|
||||||
|
/>
|
||||||
|
<div className="p-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleAddConnection}
|
||||||
|
disabled={isConnecting || !connectionUri.trim()}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{isConnecting ? 'Connecting...' : 'Connect'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
{children || (
|
{children || (
|
||||||
@ -319,8 +386,10 @@ export function WalletModal({ children, className }: WalletModalProps) {
|
|||||||
Connect your lightning wallet to send zaps instantly.
|
Connect your lightning wallet to send zaps instantly.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<WalletContent />
|
<WalletContent {...walletContentProps} />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
{addWalletDialog}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -248,8 +248,6 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (target) {
|
if (target) {
|
||||||
setComment('Zapped with MKStack!');
|
setComment('Zapped with MKStack!');
|
||||||
|
@ -10,7 +10,7 @@ export interface NWCConnection {
|
|||||||
client?: LN;
|
client?: LN;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NWCInfo {
|
export interface NWCInfo {
|
||||||
alias?: string;
|
alias?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
pubkey?: string;
|
pubkey?: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user