Implement copy functionality for LNURL and Lightning Address in WalletPage.vue

- Added a copyToClipboard function to enable users to copy LNURL and Lightning Address directly from the wallet interface.
- Enhanced the QR code display to allow clicking for copying the LNURL.
- Introduced visual feedback with icons indicating successful copy actions.

These changes improve user experience by simplifying the process of sharing wallet information.
This commit is contained in:
padreug 2025-09-18 23:00:55 +02:00
parent 5293f2f4c2
commit 27070c0390

View file

@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Badge } from '@/components/ui/badge'
import { ScrollArea } from '@/components/ui/scroll-area'
import { RefreshCw, Send, QrCode, ArrowUpRight, ArrowDownLeft, Clock, Wallet } from 'lucide-vue-next'
import { RefreshCw, Send, QrCode, ArrowUpRight, ArrowDownLeft, Clock, Wallet, Copy, Check } from 'lucide-vue-next'
import ReceiveDialog from '../components/ReceiveDialog.vue'
import SendDialog from '../components/SendDialog.vue'
import { format } from 'date-fns'
@ -16,6 +16,7 @@ import { nip19 } from 'nostr-tools'
const walletService = injectService(SERVICE_TOKENS.WALLET_SERVICE) as any
const paymentService = injectService(SERVICE_TOKENS.PAYMENT_SERVICE) as any
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE) as any
const toastService = injectService(SERVICE_TOKENS.TOAST_SERVICE) as any
// State
const showReceiveDialog = ref(false)
@ -23,6 +24,7 @@ const showSendDialog = ref(false)
const selectedTab = ref('transactions')
const defaultQrCode = ref<string | null>(null)
const isGeneratingQR = ref(false)
const copiedField = ref<string | null>(null)
// Computed
const transactions = computed(() => walletService?.transactions?.value || [])
@ -119,6 +121,31 @@ async function generateDefaultQR() {
}
}
// Copy functionality
async function copyToClipboard(text: string, field: string) {
try {
await navigator.clipboard.writeText(text)
copiedField.value = field
toastService?.success('Copied to clipboard!')
setTimeout(() => {
copiedField.value = null
}, 2000)
} catch (error) {
console.error('Failed to copy:', error)
toastService?.error('Failed to copy to clipboard')
}
}
// Click handler for QR code to copy LNURL
function handleQRClick() {
if (firstPayLink.value?.lnurl) {
// Encode LNURL with proper bech32 format and lightning: prefix (same as QR code)
const encodedLNURL = encodeLNURL(firstPayLink.value.lnurl)
copyToClipboard(encodedLNURL, 'qr-lnurl')
}
}
// Initialize on mount
onMounted(async () => {
await refresh()
@ -205,13 +232,18 @@ onMounted(async () => {
<div v-if="isGeneratingQR" class="w-48 h-48 flex items-center justify-center bg-muted rounded-lg">
<RefreshCw class="h-8 w-8 animate-spin text-muted-foreground" />
</div>
<div v-else-if="defaultQrCode" class="bg-white p-4 rounded-lg">
<div v-else-if="defaultQrCode" class="text-center">
<div class="bg-white p-4 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors" @click="handleQRClick" title="Click to copy LNURL">
<img
:src="defaultQrCode"
alt="LNURL QR Code"
class="w-48 h-48"
/>
</div>
<p class="text-xs text-muted-foreground mt-2">
Click QR code to copy LNURL
</p>
</div>
<div v-else class="w-48 h-48 flex items-center justify-center bg-muted rounded-lg">
<QrCode class="h-12 w-12 text-muted-foreground opacity-50" />
</div>
@ -228,9 +260,21 @@ onMounted(async () => {
<div v-if="firstPayLink.lnaddress">
<h4 class="font-medium mb-2">Lightning Address</h4>
<div class="font-mono text-sm bg-muted px-3 py-2 rounded">
<div class="flex gap-2">
<div class="font-mono text-sm bg-muted px-3 py-2 rounded flex-1">
{{ firstPayLink.lnaddress }}
</div>
<Button
variant="outline"
size="sm"
@click="copyToClipboard(firstPayLink.lnaddress || '', 'lightning-address')"
class="h-auto px-2"
title="Copy Lightning Address"
>
<Check v-if="copiedField === 'lightning-address'" class="h-4 w-4 text-green-600" />
<Copy v-else class="h-4 w-4" />
</Button>
</div>
</div>
<Button