Enhance SendDialog and WalletPage with QR code scanning integration
- Added initialDestination prop to SendDialog for pre-filling the destination field. - Implemented a watcher to update the destination field when initialDestination changes. - Integrated QRScanner component into WalletPage, allowing users to scan QR codes for payment destinations. - Updated SendDialog to accept scanned destination and improved user feedback with toast notifications. These changes streamline the payment process by enabling QR code scanning directly within the wallet interface.
This commit is contained in:
parent
7b240fc5be
commit
f94dc1d03c
2 changed files with 70 additions and 6 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } 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'
|
||||||
|
|
@ -14,6 +14,7 @@ import QRScanner from '@/components/ui/qr-scanner.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
initialDestination?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
|
|
@ -38,7 +39,7 @@ const formSchema = toTypedSchema(z.object({
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
validationSchema: formSchema,
|
validationSchema: formSchema,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
destination: '',
|
destination: props.initialDestination || '',
|
||||||
amount: 100,
|
amount: 100,
|
||||||
comment: ''
|
comment: ''
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +48,13 @@ const form = useForm({
|
||||||
const { resetForm, values, meta, setFieldValue } = form
|
const { resetForm, values, meta, setFieldValue } = form
|
||||||
const isFormValid = computed(() => meta.value.valid)
|
const isFormValid = computed(() => meta.value.valid)
|
||||||
|
|
||||||
|
// Watch for prop changes
|
||||||
|
watch(() => props.initialDestination, (newDestination) => {
|
||||||
|
if (newDestination) {
|
||||||
|
setFieldValue('destination', newDestination)
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const isSending = computed(() => walletService?.isSendingPayment?.value || false)
|
const isSending = computed(() => walletService?.isSendingPayment?.value || false)
|
||||||
const showScanner = ref(false)
|
const showScanner = ref(false)
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import { Button } from '@/components/ui/button'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { RefreshCw, Send, QrCode, ArrowUpRight, ArrowDownLeft, Clock, Wallet, Copy, Check } from 'lucide-vue-next'
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||||
|
import { RefreshCw, Send, QrCode, ArrowUpRight, ArrowDownLeft, Clock, Wallet, ScanLine, Copy, Check } from 'lucide-vue-next'
|
||||||
import ReceiveDialog from '../components/ReceiveDialog.vue'
|
import ReceiveDialog from '../components/ReceiveDialog.vue'
|
||||||
import SendDialog from '../components/SendDialog.vue'
|
import SendDialog from '../components/SendDialog.vue'
|
||||||
|
import QRScanner from '@/components/ui/qr-scanner.vue'
|
||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
|
|
||||||
|
|
@ -21,6 +23,8 @@ const toastService = injectService(SERVICE_TOKENS.TOAST_SERVICE) as any
|
||||||
// State
|
// State
|
||||||
const showReceiveDialog = ref(false)
|
const showReceiveDialog = ref(false)
|
||||||
const showSendDialog = ref(false)
|
const showSendDialog = ref(false)
|
||||||
|
const showQRScanner = ref(false)
|
||||||
|
const scannedDestination = ref<string>('')
|
||||||
const selectedTab = ref('transactions')
|
const selectedTab = ref('transactions')
|
||||||
const defaultQrCode = ref<string | null>(null)
|
const defaultQrCode = ref<string | null>(null)
|
||||||
const isGeneratingQR = ref(false)
|
const isGeneratingQR = ref(false)
|
||||||
|
|
@ -144,6 +148,26 @@ function handleQRClick() {
|
||||||
const encodedLNURL = encodeLNURL(firstPayLink.value.lnurl)
|
const encodedLNURL = encodeLNURL(firstPayLink.value.lnurl)
|
||||||
copyToClipboard(encodedLNURL, 'qr-lnurl')
|
copyToClipboard(encodedLNURL, 'qr-lnurl')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QR Scanner functions
|
||||||
|
function closeQRScanner() {
|
||||||
|
showQRScanner.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleQRScanResult(result: string) {
|
||||||
|
// Clean up the scanned result by removing lightning: prefix if present
|
||||||
|
let cleanedResult = result
|
||||||
|
if (result.toLowerCase().startsWith('lightning:')) {
|
||||||
|
cleanedResult = result.substring(10) // Remove "lightning:" prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the scanned result and close QR scanner
|
||||||
|
scannedDestination.value = cleanedResult
|
||||||
|
closeQRScanner()
|
||||||
|
|
||||||
|
// Open send dialog with the scanned result
|
||||||
|
showSendDialog.value = true
|
||||||
|
toastService?.success('QR code scanned successfully!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize on mount
|
// Initialize on mount
|
||||||
|
|
@ -211,6 +235,14 @@ onMounted(async () => {
|
||||||
<Send class="h-5 w-5" />
|
<Send class="h-5 w-5" />
|
||||||
Send
|
Send
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
@click="showQRScanner = true"
|
||||||
|
class="h-12 w-12 p-0"
|
||||||
|
title="Scan QR Code"
|
||||||
|
>
|
||||||
|
<ScanLine class="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
@ -517,10 +549,34 @@ onMounted(async () => {
|
||||||
@update:open="showReceiveDialog = $event"
|
@update:open="showReceiveDialog = $event"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SendDialog
|
<SendDialog
|
||||||
v-if="showSendDialog"
|
v-if="showSendDialog"
|
||||||
:open="showSendDialog"
|
:open="showSendDialog"
|
||||||
@update:open="showSendDialog = $event"
|
:initial-destination="scannedDestination"
|
||||||
|
@update:open="(open) => {
|
||||||
|
showSendDialog = open
|
||||||
|
if (!open) scannedDestination = ''
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- QR Scanner Dialog -->
|
||||||
|
<Dialog :open="showQRScanner" @update:open="showQRScanner = $event">
|
||||||
|
<DialogContent class="sm:max-w-lg">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle class="flex items-center gap-2">
|
||||||
|
<ScanLine class="h-5 w-5" />
|
||||||
|
Scan QR Code
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Point your camera at a Lightning invoice or payment QR code
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<QRScanner
|
||||||
|
@result="handleQRScanResult"
|
||||||
|
@close="closeQRScanner"
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue