Enhance ReceiveDialog and WalletService for LNURL handling and transaction tagging

- Added functionality to encode LNURL for QR code generation in ReceiveDialog, improving payment link sharing.
- Updated WalletService to include a tag property for transactions, allowing for better categorization and display in WalletPage.
- Enhanced WalletPage to display transaction tags, improving user visibility of transaction details.

These changes improve the user experience by providing clearer payment information and enhancing the functionality of the wallet module.
This commit is contained in:
padreug 2025-09-14 23:42:09 +02:00
parent 86b1710030
commit 981fc23422
3 changed files with 52 additions and 10 deletions

View file

@ -3,6 +3,7 @@ import { ref, computed, nextTick } from 'vue'
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod' import * as z from 'zod'
import { nip19 } from 'nostr-tools'
import { injectService, SERVICE_TOKENS } from '@/core/di-container' import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@ -106,13 +107,29 @@ async function copyToClipboard(text: string, field: string) {
} }
} }
function encodeLNURL(url: string): string {
try {
// Convert URL to bytes
const bytes = new TextEncoder().encode(url)
// Encode as bech32 with 'lnurl' prefix
const bech32 = nip19.encodeBytes('lnurl', bytes)
// Return with lightning: prefix in uppercase
return `lightning:${bech32.toUpperCase()}`
} catch (error) {
console.error('Failed to encode LNURL:', error)
return url // Fallback to original URL
}
}
async function generateQRCode(data: string) { async function generateQRCode(data: string) {
if (!data) return if (!data) return
isLoadingQR.value = true isLoadingQR.value = true
try { try {
// Encode LNURL with proper bech32 format and lightning: prefix
const encodedLNURL = encodeLNURL(data)
// Use the existing PaymentService QR code generation // Use the existing PaymentService QR code generation
qrCode.value = await paymentService?.generateQRCode(data) qrCode.value = await paymentService?.generateQRCode(encodedLNURL)
} catch (error) { } catch (error) {
console.error('Failed to generate QR code:', error) console.error('Failed to generate QR code:', error)
toastService?.error('Failed to generate QR code') toastService?.error('Failed to generate QR code')
@ -331,9 +348,29 @@ function onOpenChange(open: boolean) {
<QrCode class="h-12 w-12 text-muted-foreground opacity-50" /> <QrCode class="h-12 w-12 text-muted-foreground opacity-50" />
</div> </div>
<!-- LNURL --> <!-- Encoded LNURL (for QR) -->
<div class="w-full space-y-2"> <div class="w-full space-y-2">
<Label>LNURL</Label> <Label>LNURL (Encoded)</Label>
<div class="flex gap-2">
<Input
:value="selectedPayLink.lnurl ? encodeLNURL(selectedPayLink.lnurl) : ''"
readonly
class="font-mono text-xs"
/>
<Button
variant="outline"
size="sm"
@click="copyToClipboard(selectedPayLink.lnurl ? encodeLNURL(selectedPayLink.lnurl) : '', 'encoded-lnurl')"
>
<Check v-if="copiedField === 'encoded-lnurl'" class="h-4 w-4 text-green-600" />
<Copy v-else class="h-4 w-4" />
</Button>
</div>
</div>
<!-- Raw LNURL -->
<div class="w-full space-y-2">
<Label>LNURL (Raw)</Label>
<div class="flex gap-2"> <div class="flex gap-2">
<Input <Input
:value="selectedPayLink.lnurl" :value="selectedPayLink.lnurl"

View file

@ -29,6 +29,7 @@ export interface PaymentTransaction {
type: 'sent' | 'received' type: 'sent' | 'received'
status: 'pending' | 'confirmed' | 'failed' status: 'pending' | 'confirmed' | 'failed'
fee?: number fee?: number
tag?: string | null
} }
export default class WalletService extends BaseService { export default class WalletService extends BaseService {
@ -274,7 +275,8 @@ export default class WalletService extends BaseService {
timestamp: payment.time ? new Date(payment.time * 1000) : new Date(), timestamp: payment.time ? new Date(payment.time * 1000) : new Date(),
type: payment.amount > 0 ? 'received' : 'sent', type: payment.amount > 0 ? 'received' : 'sent',
status: payment.pending ? 'pending' : 'confirmed', status: payment.pending ? 'pending' : 'confirmed',
fee: payment.fee ? payment.fee / 1000 : undefined fee: payment.fee ? payment.fee / 1000 : undefined,
tag: payment.tag || (payment.extra && payment.extra.tag) || null
})).sort((a: PaymentTransaction, b: PaymentTransaction) => })).sort((a: PaymentTransaction, b: PaymentTransaction) =>
b.timestamp.getTime() - a.timestamp.getTime() b.timestamp.getTime() - a.timestamp.getTime()
) )

View file

@ -202,6 +202,9 @@ onMounted(() => {
<Badge :variant="getStatusColor(tx.status)" class="text-xs"> <Badge :variant="getStatusColor(tx.status)" class="text-xs">
{{ tx.status }} {{ tx.status }}
</Badge> </Badge>
<Badge v-if="tx.tag" variant="outline" class="text-xs">
{{ tx.tag }}
</Badge>
</div> </div>
</div> </div>
</div> </div>