commit baa2344ebbf76c1be1ddfd079ea57451041ee66e
parent 0f2ad6c051314389caa7fe9c3fe5f402f29d8f3d
Author: Marc Stibane <marc@taler.net>
Date: Thu, 7 Nov 2024 21:35:48 +0100
TransactionCommon, row
Diffstat:
7 files changed, 85 insertions(+), 42 deletions(-)
diff --git a/TalerWallet1/Model/Transaction.swift b/TalerWallet1/Model/Transaction.swift
@@ -137,6 +137,9 @@ enum TransactionMajorState: String, Codable {
struct TransactionState: Codable, Hashable {
var major: TransactionMajorState
var minor: TransactionMinorState?
+
+ var isReady: Bool { minor == .ready }
+ var isKYC: Bool { minor == .kyc || minor == .balanceKyc || minor == .mergeKycRequired }
}
struct TransactionTransition: Codable { // Notification
@@ -284,20 +287,29 @@ enum TransactionType: String, Codable {
}
}
+struct KycAuthTransferInfo: Decodable, Sendable {
+ /// The KYC auth transfer will *not* work if it originates from a different account.
+ var debitPaytoUri: String /// Payto URI of the account that must make the transfer
+ var accountPub: String /// Account public key that must be included in the subject
+ var creditPaytoUris: [String] /// Possible target payto URIs
+}
+
struct TransactionCommon: Decodable, Sendable {
var type: TransactionType
- var txState: TransactionState
- var amountEffective: Amount
- var amountRaw: Amount
var transactionId: String
var timestamp: Timestamp
var scopes: [ScopeInfo]
+ var txState: TransactionState
var txActions: [TxAction]
+ var amountRaw: Amount
+ var amountEffective: Amount
+ var error: TalerErrorDetail?
var kycUrl: String?
+ var kycAuthTransferInfo: KycAuthTransferInfo?
var isPending : Bool { txState.major == .pending }
- var isPendingReady : Bool { isPending && txState.minor == .ready }
- var isPendingKYC : Bool { isPending && (txState.minor == .kyc || txState.minor == .balanceKyc || txState.minor == .mergeKycRequired) }
+ var isPendingReady : Bool { isPending && txState.isReady }
+ var isPendingKYC : Bool { isPending && txState.isKYC }
var isDone : Bool { txState.major == .done }
var isAborting : Bool { txState.major == .aborting }
var isAborted : Bool { txState.major == .aborted }
diff --git a/TalerWallet1/Model/WalletModel.swift b/TalerWallet1/Model/WalletModel.swift
@@ -10,6 +10,12 @@ import os.log
fileprivate let DATABASE = "talerwalletdb-v30"
fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for debugging
+struct TalerErrorDetail: Codable, Hashable {
+ var code: Int
+ var when: Timestamp?
+ var hint: String?
+}
+
struct HTTPError: Codable, Hashable {
var code: Int
var requestUrl: String?
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -251,13 +251,13 @@ struct ManualDetailsV: View {
struct ManualDetails_Previews: PreviewProvider {
static var previews: some View {
let common = TransactionCommon(type: .withdrawal,
- txState: TransactionState(major: .done),
- amountEffective: Amount(currency: LONGCURRENCY, cent: 110),
- amountRaw: Amount(currency: LONGCURRENCY, cent: 220),
transactionId: "someTxID",
timestamp: Timestamp(from: 1_666_666_000_000),
scopes: [],
- txActions: [])
+ txState: TransactionState(major: .done),
+ txActions: [],
+ amountRaw: Amount(currency: LONGCURRENCY, cent: 220),
+ amountEffective: Amount(currency: LONGCURRENCY, cent: 110))
let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"
let details = WithdrawalDetails(type: .manual,
reservePub: "ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl",
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsWireV.swift b/TalerWallet1/Views/Transactions/ManualDetailsWireV.swift
@@ -173,14 +173,14 @@ struct ManualDetailsWireV: View {
//struct ManualDetailsWire_Previews: PreviewProvider {
// static var previews: some View {
// let common = TransactionCommon(type: .withdrawal,
-// txState: TransactionState(major: .done),
-// amountEffective: Amount(currency: LONGCURRENCY, cent: 110),
-// amountRaw: Amount(currency: LONGCURRENCY, cent: 220),
// transactionId: "someTxID",
// timestamp: Timestamp(from: 1_666_666_000_000),
+// txState: TransactionState(major: .done),
// txActions: [])
+// amountEffective: Amount(currency: LONGCURRENCY, cent: 110),
+// amountRaw: Amount(currency: LONGCURRENCY, cent: 220),
// let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"
-// let details = WithdrawalDetails(type: .manual,
+// let details = WithdrawalDetails(type: .manual,
// reservePub: "ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl",
// reserveIsReady: false,
// confirmed: false)
diff --git a/TalerWallet1/Views/Transactions/ThreeAmountsSection.swift b/TalerWallet1/Views/Transactions/ThreeAmountsSection.swift
@@ -182,14 +182,13 @@ struct ThreeAmounts_Previews: PreviewProvider {
var body: some View {
let scope = ScopeInfo.zero(LONGCURRENCY)
let common = TransactionCommon(type: .withdrawal,
- txState: TransactionState(major: .done),
- amountEffective: Amount(currency: LONGCURRENCY, cent: 10),
- amountRaw: Amount(currency: LONGCURRENCY, cent: 20),
transactionId: "someTxID",
timestamp: Timestamp(from: 1_666_666_000_000),
scopes: [scope],
+ txState: TransactionState(major: .done),
txActions: [],
- kycUrl: nil)
+ amountRaw: Amount(currency: LONGCURRENCY, cent: 20),
+ amountEffective: Amount(currency: LONGCURRENCY, cent: 10))
// let test = Amount(currency: TESTCURRENCY, cent: 123)
// let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
List {
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -15,23 +15,39 @@ struct TransactionRowView: View {
@Environment(\.sizeCategory) var sizeCategory
@Environment(\.colorScheme) private var colorScheme
@Environment(\.colorSchemeContrast) private var colorSchemeContrast
+ @AppStorage("minimalistic") var minimalistic: Bool = false
func needVStack(available: CGFloat, contentWidth: CGFloat, valueWidth: CGFloat) -> Bool {
available < (contentWidth + valueWidth + 40)
}
- func topString() -> String {
+ func topString(forA11y: Bool = false) -> String? {
switch transaction {
case .payment(let paymentTransaction):
return paymentTransaction.details.info.merchant.name
default:
- return transaction.isDone ? transaction.localizedTypePast
- : transaction.localizedType
+ let result = transaction.isDone ? transaction.localizedTypePast
+ : transaction.localizedType
+ return forA11y ? result
+ : minimalistic ? nil
+ : result
+ }
+ }
+
+ func amount() -> Amount {
+ switch transaction {
+ case .refresh(let refreshTransaction):
+ let details = refreshTransaction.details
+ return details.refreshInputAmount
+ default:
+ let common = transaction.common
+ let eff = common.amountEffective
+ if !eff.isZero { return eff }
+ return common.amountRaw
}
}
var body: some View {
- let common = transaction.common
let pending = transaction.isPending
let needsKYC = transaction.isPendingKYC
let shouldConfirm = transaction.shouldConfirm
@@ -40,13 +56,13 @@ struct TransactionRowView: View {
let increasedContrast = colorSchemeContrast == .increased
let details = transaction.detailsToShow()
let keys = details.keys
-
+ let common = transaction.common
+ let isZero = common.amountEffective.isZero
let incoming = common.incoming()
let textColor = doneOrPending ? .primary
: colorScheme == .dark ? .secondary
: increasedContrast ? Color(.darkGray)
: .secondary // Color(.tertiaryLabel)
- let isZero = common.amountEffective.isZero
let refreshZero = common.type.isRefresh && isZero
let foreColor = refreshZero ? textColor
: pending ? WalletColors().pendingColor(incoming)
@@ -56,18 +72,20 @@ struct TransactionRowView: View {
let iconBadge = TransactionIconBadge(type: common.type, foreColor: foreColor,
done: done, incoming: incoming,
shouldConfirm: shouldConfirm, needsKYC: needsKYC)
- let amountV = AmountV(scope, isZero ? common.amountRaw : common.amountEffective,
- isNegative: isZero ? nil : !incoming, strikethrough: !doneOrPending)
+ let amountV = AmountV(scope, amount(), isNegative: isZero ? nil : !incoming,
+ strikethrough: !doneOrPending)
.foregroundColor(foreColor)
+ let topA11y = topString(forA11y: true)
let topString = topString()
- let centerTop = Text(topString)
+ let centerTop = Text(topString ?? EMPTYSTRING)
.foregroundColor(textColor)
.strikethrough(!doneOrPending, color: .red)
.talerFont(.headline)
// .fontWeight(.medium) iOS 16
.padding(.bottom, -2.0)
- .accessibilityLabel(doneOrPending ? topString : topString + String(localized: ", canceled", comment: "VoiceOver"))
+ .accessibilityLabel(doneOrPending ? topA11y!
+ : topA11y! + String(localized: ", canceled", comment: "VoiceOver"))
let centerBottom = TimelineView(.everyMinute) { context in
let (dateString, date) = TalerDater.dateString(from: common.timestamp, relative: true)
Text(dateString)
@@ -85,7 +103,7 @@ struct TransactionRowView: View {
let layout1 = HStack(spacing: 4) {
VStack(alignment: .leading, spacing: 2) {
- centerTop
+ if topString != nil { centerTop }
centerBottom
}
#if DEBUG
@@ -96,7 +114,7 @@ struct TransactionRowView: View {
}
let layout2 = VStack(alignment: .leading, spacing: 2) {
- centerTop
+ if topString != nil { centerTop }
HStack(spacing: 6) {
centerBottom
#if DEBUG
@@ -111,16 +129,24 @@ struct TransactionRowView: View {
}
let layout3 = VStack(spacing: 0) {
- HStack(spacing: 8) {
+ if topString != nil {
+ HStack(spacing: 8) {
centerTop
Spacer(minLength: 2)
- amountV
+ amountV
+ }
+ centerBottom
+ } else {
+ HStack(spacing: 8) {
+ centerBottom
+ Spacer(minLength: 2)
+ amountV
+ }
}
- centerBottom
}
let layout4 = VStack(alignment: .leading, spacing: 2) {
- centerTop
+ if topString != nil { centerTop }
HStack(spacing: -4) {
Spacer(minLength: 2)
#if DEBUG
@@ -207,15 +233,13 @@ extension Transaction { // for PreViews
let raw = Amount(currency: LONGCURRENCY, cent: 500)
let eff = Amount(currency: LONGCURRENCY, cent: incoming ? 480 : 520)
let common = TransactionCommon(type: incoming ? .withdrawal : .payment,
- txState: TransactionState(major: pending ? TransactionMajorState.pending
- : TransactionMajorState.done),
- amountEffective: eff,
- amountRaw: raw,
transactionId: id,
timestamp: time,
scopes: [],
+ txState: txState,
txActions: [.abort],
- kycUrl: nil)
+ amountRaw: raw,
+ amountEffective: eff)
if incoming {
// if pending then manual else bank-integrated
let payto = "payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company&amount=KUDOS%3A9.99&message=Taler+Withdrawal+J41FQPJGAP1BED1SFSXHC989EN8HRDYAHK688MQ228H6SKBMV0AG"
diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryV.swift b/TalerWallet1/Views/Transactions/TransactionSummaryV.swift
@@ -14,14 +14,13 @@ extension Transaction { // for Dummys
let amount = Amount.zero(currency: dummyCurrency)
let now = Timestamp.now()
let common = TransactionCommon(type: .dummy,
- txState: TransactionState(major: .pending),
- amountEffective: amount,
- amountRaw: amount,
transactionId: EMPTYSTRING,
timestamp: now,
scopes: [],
+ txState: TransactionState(major: .pending),
txActions: [],
- kycUrl: nil)
+ amountRaw: amount,
+ amountEffective: amount)
self = .dummy(DummyTransaction(common: common))
}
}
@@ -44,6 +43,7 @@ struct TransactionSummaryV: View {
@Environment(\.colorScheme) private var colorScheme
@Environment(\.colorSchemeContrast) private var colorSchemeContrast
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
+ @AppStorage("minimalistic") var minimalistic: Bool = false
@AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
#if DEBUG
@AppStorage("developerMode") var developerMode: Bool = true
@@ -118,6 +118,7 @@ struct TransactionSummaryV: View {
let pending = transaction.isPending
let locale = TalerDater.shared.locale
let (dateString, date) = TalerDater.dateString(from: common.timestamp)
+// let (dateString, date) = TalerDater.dateString(common.timestamp, minimalistic)
let a11yDate = TalerDater.accessibilityDate(date) ?? dateString
let navTitle2 = transaction.isDone ? transaction.localizedTypePast
: transaction.localizedType
@@ -456,6 +457,7 @@ struct TransactionSummaryV: View {
if !transaction.isDone {
let expiration = details.info.expiration
let (dateString, date) = TalerDater.dateString(from: expiration)
+// let (dateString, date) = TalerDater.dateString(expiration, minimalistic)
let a11yDate = TalerDater.accessibilityDate(date) ?? dateString
let a11yLabel = String(localized: "Expires: \(a11yDate)", comment: "VoiceOver")
Text("Expires: \(dateString)")