taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 5ec620ec4bb310cff2588e37e171ab49ab3c37c1
parent c3d3295ee7f96215077b4b796b4b85c74add3ca0
Author: Marc Stibane <marc@taler.net>
Date:   Wed, 16 Apr 2025 01:32:00 +0200

compactStacks

Diffstat:
MTalerWallet1/Views/OIM/OIMcash.swift | 135++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
MTalerWallet1/Views/OIM/OIMlayout.swift | 12++++++------
MTalerWallet1/Views/OIM/OIMlineView.swift | 2+-
3 files changed, 92 insertions(+), 57 deletions(-)

diff --git a/TalerWallet1/Views/OIM/OIMcash.swift b/TalerWallet1/Views/OIM/OIMcash.swift @@ -18,7 +18,7 @@ enum FundState: Int { } /// data structure for a cash item on the table -public struct OIMfund: Identifiable, Equatable, Hashable { +public struct OIMfund: Identifiable, Equatable, Hashable, Sendable { public let id: Int // support multiple funds with the same value let value: UInt64 var state: FundState @@ -55,7 +55,7 @@ class OIMcash: ObservableObject { ( ($0.value == $1.value) && ($0.id > $1.id) ) }) } - func checkStacks(_ max: Int = MAXSTACK) -> UInt64? { + func checkStacks(first firstCheck: UInt64, _ max: Int = MAXSTACK) -> UInt64? { /// same algorithm as OIMlayout.computeSpaces /// result is 0 if all stacks have 4 or less items /// returns highest value with more than 4 items @@ -83,51 +83,86 @@ class OIMcash: ObservableObject { return nil } -// func compactStacks(_ value: UInt64) -> Bool { -// let denominations = currency.bankNotes + currency.bankCoins -// if let index = denominations.firstIndex(where: { $0 == value }) { -// if index > 0 { // does a bigger denomination exist? -// let nextIndex = index-1 -// let nextValue = denominations[nextIndex] // let next = next_bigger_denomination -// if nextValue != value * 5 { -// if nextValue.isMultiple(of: value) { // counter example: value=10, next=25 -// let hasNextIdx = funds.firstIndex(where: { $0.value == nextValue }) -// let has5xDenom -// // if funds.contains(next) -// -// // || denom(5*value) doesn't exist e.g. 50->2.50 or 25->1.25 -// // flip&remove X items of value, insert 1 next -// -// -// // let next2 = even_bigger_denomination -// -// // else if funds.contains(next2) -// // -// // else -// // flip&remove 5 items of value, insert 1 next -// -// -// -// -// -// } -// -// -// var newVal = 5 * value -// if currency.bankCoins.contains(newVal) || -// currency.bankNotes.contains(newVal) -// { -// let sorted = sortByValue() -// // find 5 cash items of 'value' -// var toRemove: OIMfunds = [] -// -// } -// if { -// -// return true -// } -// return false -// } + func convert(_ nr: UInt64, of stackValue: UInt64, to biggerDenom: UInt64) -> Bool { + let sorted = sortByValue() + var counter = nr + var toDelete: OIMfunds = [] + var amount: UInt64 = 0 + for var fund in sorted { + if fund.value == stackValue && counter > 0 { + withAnimation(.basic1) { + fund.state = .flipped + updateFund(fund) + } + toDelete.append(fund) + counter -= 1 + amount += stackValue + } + } + DispatchQueue.main.async { + withAnimation(.remove1) { + for fund in toDelete { + self.removeCash(id: fund.id, value: fund.value) + } + } + withAnimation(.basic1) { + while amount >= biggerDenom { + self.addCash(value: biggerDenom, .flipped) + amount -= biggerDenom + } + } + if amount > 0 { + print(" ❗️Yikes: convert failed", amount) + } + } + return counter == 0 + } + + func compactStacks(_ value: UInt64) -> Bool { + let denominations = currency.bankNotes + currency.bankCoins + if let index = denominations.firstIndex(where: { $0 == value }) { + if index > 0 { // does a bigger denomination exist? + let value5x = value * 5 + let nextIndex = index-1 + let nextValue = denominations[nextIndex] // let next = next_bigger_denomination + if nextValue == value5x { + return convert(5, of: value, to: nextValue) + } + // since we want to "convert" adjacent denominations (some smaller will become 1 larger fund), + // we cannot use the whole 5 smaller funds (4 in stack, 1 just added) if next is not 5 times bigger + // check whether there are any items of next already on the table + let hasNextIdx = funds.firstIndex(where: { $0.value == nextValue }) + if hasNextIdx == nil { // no, then... + if index > 1 { // check the second bigger denomination + let secondIdx = index-2 + let secondValue = denominations[secondIdx] + if secondValue <= value5x && secondValue.isMultiple(of: value) { + let nrToDelete = secondValue / value + return convert(nrToDelete, of: value, to: secondValue) + } + } + } + // If we arrive here, either there is no second denomination any more, or it is not a multiple of value, + // or there are already some funds of next on the table + if nextValue.isMultiple(of: value) { + let nrToDelete = nextValue / value + return convert(nrToDelete, of: value, to: nextValue) + } + // Now this is tricky - there are already some funds of next on the table, but next is not a multiple of value + // But maybe 2 of next are a multiple - e.g. value=10, next=25 + let nextValue2x = nextValue * 2 + if nextValue2x.isMultiple(of: value) { + let nrToDelete = nextValue * 2 / value + if nrToDelete <= 5 { + return convert(nrToDelete, of: value, to: nextValue) // 5*10 = 2*25 + } + } + } + // It seems we cannot merge with adjacent funds :-( + // Or there is no "next", the user already has the highest denomination + } + return false + } func notes() -> OIMfunds { let firstCoinVal = currency.bankCoins[0] @@ -139,14 +174,14 @@ class OIMcash: ObservableObject { return funds.filter { $0.value <= firstCoinVal } } - func addCash(_ value: UInt64) { - let fund = OIMfund(id: ticker, value: value, state: .shouldFly) + func addCash(value: UInt64, _ newState: FundState = .shouldFly) { + let fund = OIMfund(id: ticker, value: value, state: newState) ticker += 1 funds.append(fund) } func removeCash(id: Int, value: UInt64) { - if let index = funds.firstIndex(where: { $0.id == id }) { + if let index = funds.firstIndex(where: { $0.id == id && $0.value == value }) { funds.remove(at: index) } else if let index = funds.lastIndex(where: { $0.value == value && $0.state == .flipped }) { funds.remove(at: index) diff --git a/TalerWallet1/Views/OIM/OIMlayout.swift b/TalerWallet1/Views/OIM/OIMlayout.swift @@ -125,12 +125,12 @@ struct OIMlayoutView: View { if value > 0 { print("*** check:", value) var firstCheck = value -// while let moreThan4 = cash.checkStacks(firstCheck) { -// firstCheck = 0 + if let moreThan4 = cash.checkStacks(first: firstCheck) { + firstCheck = 0 // withAnimation(.fly1) { -// cash.compact(moreThan4) + cash.compactStacks(moreThan4) // } -// } + } checkStacks = 0 } } @@ -262,7 +262,7 @@ struct OIMlayout: Layout { default: if var spaceToFill = proposal.width { if var heightToFill = proposal.height { - print("Yikes❗️ width + height:", spaceToFill, heightToFill) + // print("Yikes❗️ width + height:", spaceToFill, heightToFill) // // TODO: compute the inner-stack offsets // spaceToFill -= viewWidths // might be negative } else { // unspecified @@ -281,7 +281,7 @@ struct OIMlayout: Layout { } let result = CGSize(width: viewWidths + accumulatedSpaces, height: maxHeight + stackHeight) - print(" sizeThatFits:", result) +// print(" ***sizeThatFits:", result) return result } diff --git a/TalerWallet1/Views/OIM/OIMlineView.swift b/TalerWallet1/Views/OIM/OIMlineView.swift @@ -123,7 +123,7 @@ struct OIMlineView: View { withAnimation(.fly1) { if #available(iOS 16.4, *) { print("\n>>addCash", newVal) - cash.addCash(newVal) + cash.addCash(value: newVal) amountVal += newVal // update directly } else { print("\n>>start flying", newVal, flying)