Add Tap to Pay feature and misc fixes
Adds BML contactless token fetching, APDU emulation utility, TapToPayViewModel, and TapToPayView with NFC animation. Also updates QR scanner, transfer flow, dashboard, and home views. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,9 @@ import SwiftUI
|
||||
struct DashboardView: View {
|
||||
@Environment(HomeViewModel.self) private var vm
|
||||
@Environment(AppViewModel.self) private var app
|
||||
@State private var showQRScanner = false
|
||||
@State private var scannedCardAccountNumber: String? = nil
|
||||
@State private var tapToPayAccount: BankAccount? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -118,8 +121,7 @@ struct DashboardView: View {
|
||||
// ── Fixed bottom quick-action bar ────────────────────────────────
|
||||
HStack(spacing: 8) {
|
||||
QuickActionButton(icon: "arrow.up.right", label: "Transfer") {}
|
||||
QuickActionButton(icon: "qrcode", label: "PayMV QR") {}
|
||||
}
|
||||
QuickActionButton(icon: "qrcode", label: "PayMV QR") { showQRScanner = true } }
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, max(16, 0))
|
||||
@@ -128,6 +130,16 @@ struct DashboardView: View {
|
||||
.navigationTitle("Thijooree")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbarItems }
|
||||
.sheet(isPresented: $showQRScanner) {
|
||||
QRScannerSheet { qrContent in
|
||||
handleQR(qrContent)
|
||||
}
|
||||
}
|
||||
.sheet(item: $tapToPayAccount) { account in
|
||||
TapToPayView(account: account) {
|
||||
tapToPayAccount = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +149,12 @@ struct DashboardView: View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 0) {
|
||||
ForEach(vm.cardAccounts) { card in
|
||||
CardCarouselItem(account: card, hide: vm.hideAmounts)
|
||||
CardCarouselItem(account: card, hide: vm.hideAmounts, onScanToPay: {
|
||||
scannedCardAccountNumber = card.accountNumber
|
||||
showQRScanner = true
|
||||
}, onTapToPay: {
|
||||
tapToPayAccount = card
|
||||
})
|
||||
.padding(.trailing, 12)
|
||||
}
|
||||
}
|
||||
@@ -168,6 +185,26 @@ struct DashboardView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - QR handler
|
||||
|
||||
private func handleQR(_ content: String) {
|
||||
let bmlUrl = PaymvQrParser.extractBmlGatewayUrl(content)
|
||||
if content.hasPrefix("https://ebanking.bankofmaldives.com.mv/qrpay/") || bmlUrl != nil {
|
||||
vm.pendingBmlQrUrl = bmlUrl ?? content
|
||||
vm.pendingQrFromCard = scannedCardAccountNumber
|
||||
return
|
||||
}
|
||||
|
||||
if let result = PaymvQrParser.parse(content) {
|
||||
vm.pendingTransferQRData = result
|
||||
vm.pendingQrFromCard = scannedCardAccountNumber
|
||||
} else {
|
||||
let fallback = PaymvQrResult(account: content, amount: nil, merchantName: nil, purpose: nil)
|
||||
vm.pendingTransferQRData = fallback
|
||||
vm.pendingQrFromCard = scannedCardAccountNumber
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Empty state
|
||||
|
||||
private var emptyState: some View {
|
||||
@@ -232,6 +269,8 @@ private struct BalanceCard: View {
|
||||
struct CardCarouselItem: View {
|
||||
let account: BankAccount
|
||||
let hide: Bool
|
||||
let onScanToPay: () -> Void
|
||||
let onTapToPay: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -274,8 +313,8 @@ struct CardCarouselItem: View {
|
||||
|
||||
// Action buttons
|
||||
HStack(spacing: 4) {
|
||||
CardActionButton(icon: "qrcode", label: "Scan to Pay") {}
|
||||
CardActionButton(icon: "wave.3.right", label: "Tap to Pay") {}
|
||||
CardActionButton(icon: "qrcode", label: "Scan to Pay") { onScanToPay() }
|
||||
CardActionButton(icon: "wave.3.right", label: "Tap to Pay") { onTapToPay() }
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.top, 8)
|
||||
|
||||
Reference in New Issue
Block a user