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:
2026-06-08 01:27:27 +05:00
co-authored by Claude Sonnet 4.6
parent 160ef97e68
commit 2bbb280380
10 changed files with 1080 additions and 49 deletions
@@ -168,6 +168,16 @@ struct TransferView: View {
selectContact(contact)
homeVM.pendingTransferContact = nil
}
.onChange(of: homeVM.pendingTransferQRData) { _, qrData in
guard let qrData else { return }
handleQRFromDashboard(qrData)
homeVM.pendingTransferQRData = nil
}
.onChange(of: homeVM.pendingBmlQrUrl) { _, url in
guard let url else { return }
handleBmlQrFromDashboard(url)
homeVM.pendingBmlQrUrl = nil
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
@@ -655,6 +665,53 @@ struct TransferView: View {
}
}
private func handleQRFromDashboard(_ result: PaymvQrResult) {
setToInput(result.account)
vm.lookupState = .idle
if let amount = result.amount, !amount.isEmpty {
amountInputText = amount
vm.amountInput = amount
}
if let purpose = result.purpose, !purpose.isEmpty {
purposeInputText = purpose
vm.purposeInput = purpose
} else if let name = result.merchantName, !name.isEmpty {
purposeInputText = "Pay \(name)"
vm.purposeInput = purposeInputText
}
if let cardNumber = homeVM.pendingQrFromCard {
if let cardAccount = homeVM.accounts.first(where: {
$0.accountNumber == cardNumber &&
($0.profileType == "BML_PREPAID" || $0.profileType == "BML_CREDIT" || $0.profileType == "BML_DEBIT")
}) {
vm.fromAccount = cardAccount
}
homeVM.pendingQrFromCard = nil
}
if vm.fromAccount?.bank == "MIB" {
Task { await vm.lookupBeneficiary() }
} else if vm.fromAccount?.bank == "BML" {
Task { await vm.lookupBeneficiary() }
}
}
private func handleBmlQrFromDashboard(_ url: String) {
setToInput(url)
vm.lookupState = .idle
if let cardNumber = homeVM.pendingQrFromCard {
if let cardAccount = homeVM.accounts.first(where: {
$0.accountNumber == cardNumber &&
($0.profileType == "BML_PREPAID" || $0.profileType == "BML_CREDIT" || $0.profileType == "BML_DEBIT")
}) {
vm.fromAccount = cardAccount
}
homeVM.pendingQrFromCard = nil
}
}
private func destinationAccountRow(_ account: BankAccount) -> some View {
HStack(spacing: 12) {
ZStack {