commit 848ae7c195fa3fdc5b0c9ca035e5f586a4832d11
parent 0596238b64f2d087fcec075b4de88f7b14dcbee9
Author: Marc Stibane <marc@taler.net>
Date: Mon, 28 Sep 2026 22:24:44 +0200
Error handling w. animations
Diffstat:
3 files changed, 61 insertions(+), 19 deletions(-)
diff --git a/TalerWallet1/Views/Transactions/PaymentTransactionView.swift b/TalerWallet1/Views/Transactions/PaymentTransactionView.swift
@@ -75,6 +75,7 @@ struct PaymentTransactionView: View {
let paymentTransaction: PaymentTransaction
@Binding var scope: ScopeInfo?
@Binding var effective: Amount?
+ @Binding var possible: Bool?
@Binding var payNow: Bool
@Binding var selectedChoice: Int?
@@ -105,7 +106,10 @@ struct PaymentTransactionView: View {
if selectionDetail.status == .paymentPossible {
selectedChoice = select
effective = selectionDetail.amountEffective
+ possible = true
return select
+ } else {
+ possible = false
}
return nil
}
@@ -167,9 +171,11 @@ struct PaymentTransactionView: View {
if let newValue, newValue < choices.count {
let newChoice = choices[newValue]
effective = newChoice.0.amountEffective
+ possible = newChoice.0.status == .paymentPossible
scope = newChoice.0.scopeInfo
} else {
effective = nil
+ possible = nil
scope = nil
}
}
diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryList.swift b/TalerWallet1/Views/Transactions/TransactionSummaryList.swift
@@ -57,8 +57,10 @@ struct TransactionSummaryList: View {
@State private var viewId = UUID()
@State private var selectedChoice: Int? = nil
@State private var effective: Amount? = nil
+ @State private var paymentPossible: Bool? = nil
@State private var scope: ScopeInfo? = nil
@State private var payNow: Bool = false
+ @State private var isWorking: Bool = false
@Namespace var topID
func loadTransaction() async {
@@ -67,11 +69,11 @@ struct TransactionSummaryList: View {
symLog.log("reloaded \(reloadedTransaction.localizedType): \(reloadedTransaction.common.txState.major)")
withAnimation {
talerTX = reloadedTransaction;
+ isWorking = reloadedTransaction.common.isWorking
#if DEBUG
// controller.playSound(1104, useSystem: true)
#endif
scope = reloadedTransaction.common.scopes.first
- viewId = UUID() // redraw
}
if developerMode {
if let json = try? await model.jsonTransactionById(transactionId,
@@ -82,7 +84,11 @@ struct TransactionSummaryList: View {
}
}
} else {
- withAnimation{ talerTX = TalerTransaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() }
+ withAnimation{
+ talerTX = TalerTransaction(dummyCurrency: DEMOCURRENCY);
+ isWorking = false
+ viewId = UUID()
+ }
jsonTransaction = EMPTYSTRING
}
}
@@ -158,7 +164,11 @@ struct TransactionSummaryList: View {
} else {
Task { // runs on MainActor
// flush the screen first, then reload
- withAnimation { talerTX = TalerTransaction(dummyCurrency: DEMOCURRENCY); viewId = UUID() }
+ withAnimation {
+ talerTX = TalerTransaction(dummyCurrency: DEMOCURRENCY);
+ isWorking = false
+ viewId = UUID()
+ }
if newMajor != .none { // don't reload after delete
symLog.log("newState: \(newMajor), reloading transaction, for: \(message)")
await loadTransaction()
@@ -266,14 +276,20 @@ struct TransactionSummaryList: View {
} // Delete button
}
+ var possibleStr: String {
+ if let possible = paymentPossible {
+ return possible ? String(localized: "Confirm Payment", comment:"pay merchant navTitle")
+ : String(localized: "Payment not possible", comment:"pay merchant navTitle")
+ }
+ return String(localized: "Payment", comment:"pay merchant navTitle")
+ }
var navTitleStr: String {
let isPaying = talerTX.isPayment && talerTX.isDialog
let isRepurchase = talerTX.common.isRepurchase
return talerTX.isDone ? talerTX.localizedTypePast
- : isPaying ? (effective != nil ? String(localized: "Confirm Payment", comment:"pay merchant navTitle")
- : String(localized: "Payment not possible", comment:"pay merchant navTitle"))
- : isRepurchase ? String(localized: "Already paid", comment:"repurchase merchant navTitle")
- : talerTX.localizedType
+ : isPaying ? possibleStr
+ : isRepurchase ? String(localized: "Already paid", comment:"repurchase merchant navTitle")
+ : talerTX.localizedType
}
var body: some View {
@@ -292,12 +308,13 @@ struct TransactionSummaryList: View {
if developerMode && withActions { suspendResume(common) }
if !isPaying {
Section {
- VStack(alignment: .leading) {
- dateAndStatus(common)
- .listRowSeparator(.hidden)
- .talerFont(.title)
- }.overlay {
- if common.isWorking {
+ HStack {
+ VStack(alignment: .leading) {
+ dateAndStatus(common)
+ .listRowSeparator(.hidden)
+ .talerFont(.title)
+ }
+ if isWorking {
#if DEBUG
let background = developerMode ? Color.red : nil
#else
@@ -306,12 +323,13 @@ struct TransactionSummaryList: View {
RotatingTaler(size: 80, progress: true, once: false,
background: background,
rotationEnabled: Binding.constant(true))
+ .transition(.opacity.combined(with: .scale))
}
}
}
}
if controller.stalledConnection {
- StalledView()
+// StalledView() => show this in the safeArea
} else {
TransactionTypeDetail(stack: stack.push(),
transaction: $talerTX,
@@ -319,6 +337,7 @@ struct TransactionSummaryList: View {
selectedChoice: $selectedChoice,
scope: $scope,
effective: $effective, // return amount for "Pay <amount> now"
+ possible: $paymentPossible,
hasDone: hasDone)
// TODO: Retry Countdown, Retry Now button
@@ -334,6 +353,9 @@ struct TransactionSummaryList: View {
.navigationBarBackButtonHidden(hasDone)
.interactiveDismissDisabled(hasDone && !isPaying) // can only use "Done" button to dismiss
.safeAreaInset(edge: .bottom) {
+ let cancelButton = Button("Cancel") { dismissTop(stack.push()) }
+ .buttonStyle(TalerButtonStyle(type: .bordered))
+ .padding(is26 ? .horizontal : .all)
let bottomButton = ZStack {
if isPaying, case .payment(let paymentTransaction) = talerTX {
let details = paymentTransaction.details
@@ -346,9 +368,7 @@ struct TransactionSummaryList: View {
amountA11y: formatted.1,
payNow: $payNow)
} else {
- Button("Cancel") { dismissTop(stack.push()) }
- .buttonStyle(TalerButtonStyle(type: .bordered))
- .padding(is26 ? .horizontal : .all)
+ cancelButton
} // Cancel
} else if let showDone {
Button("Done") { dismissTop(stack.push()) }
@@ -356,8 +376,22 @@ struct TransactionSummaryList: View {
.padding(is26 ? .horizontal : .all)
}
}
- bottomButton
- .padding(is26 ? .horizontal : .all)
+ if controller.stalledConnection {
+ VStack {
+ StalledView()
+ cancelButton
+ .padding(.bottom)
+ }
+ .background {
+ SelectedBackground(useColor: false)
+ }
+ .padding(is26 ? .horizontal : .all)
+ .transition(.move(edge: .bottom))
+ } else {
+ bottomButton
+ .padding(is26 ? .horizontal : .all)
+ .transition(.scale.combined(with: .opacity))
+ }
}
.navigationTitle(navTitle ?? navTitleStr)
.onChange(of: payNow) { payNow2 in
diff --git a/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift b/TalerWallet1/Views/Transactions/TransactionTypeDetail.swift
@@ -18,6 +18,7 @@ struct TransactionTypeDetail: View {
@Binding var selectedChoice: Int?
@Binding var scope: ScopeInfo?
@Binding var effective: Amount?
+ @Binding var possible: Bool?
let hasDone: Bool
@EnvironmentObject private var controller: Controller
@@ -146,6 +147,7 @@ struct TransactionTypeDetail: View {
paymentTransaction: paymentTransaction,
scope: $scope,
effective: $effective,
+ possible: $possible,
payNow: $payNow,
selectedChoice: $selectedChoice)
.task(id: common.txState.hashValue) {