OIMcurrencyButton.swift (5949B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import SwiftUI 9 import taler_swift 10 11 fileprivate 12 struct OIMbuttonStyle: ButtonStyle { 13 14 public func makeBody(configuration: OIMbuttonStyle.Configuration) -> some View { 15 configuration.label 16 // .foregroundColor(.white) 17 // .padding(15) 18 // .background(RoundedRectangle(cornerRadius: 5).fill(color)) 19 // .compositingGroup() 20 // .shadow(color: .black, radius: 3) 21 .opacity(configuration.isPressed ? 0.7 : 1.0) 22 .scaleEffect(configuration.isPressed ? 0.9 : 1.0) 23 } 24 } 25 26 fileprivate 27 struct OIMcurrencyImage { 28 let image: Image 29 let oWidth: CGFloat 30 let oHeight: CGFloat 31 32 init(_ name: String?) { 33 if let name, let uiImage = UIImage(named: name) { 34 self.oWidth = uiImage.size.width 35 self.oHeight = uiImage.size.height 36 self.image = Image(uiImage: uiImage) 37 } else { 38 self.image = Image(systemName: "exclamationmark.triangle") 39 self.oWidth = 300 40 self.oHeight = 300 41 } 42 } 43 } 44 45 fileprivate 46 struct OIMmod: AnimatableModifier { 47 let name: String? 48 let flippedName: String? 49 let flipTime: TimeInterval 50 let isFlipped: Bool 51 let flipBack: Bool // if true, then don't animate flipping back 52 let disabled: Bool 53 let unavailable: Bool 54 let invisible: Bool 55 var pct: CGFloat 56 let action: () -> Void 57 58 var animatableData: CGFloat { 59 get { pct } 60 set { pct = newValue } 61 } 62 63 func body(content: Content) -> some View { 64 let currencyImage = OIMcurrencyImage(name) 65 let oWidth = currencyImage.oWidth / 4 66 let oHeight = currencyImage.oHeight / 4 67 let image = currencyImage.image 68 .resizable() 69 .scaledToFit() 70 71 let valueImage = image 72 .rotation3DEffect(.degrees(isFlipped ? 90 : 0), 73 axis: (x: 0.0, y: 1.0, z: 0.0)) 74 let backImage = Group { 75 if let flippedName { 76 let flippedImage = OIMcurrencyImage(flippedName) 77 flippedImage.image 78 .resizable() 79 .scaledToFit() 80 } else { 81 EmptyView() 82 } 83 } 84 .rotation3DEffect(.degrees(isFlipped ? 0 : -90), 85 axis: (x: 0.0, y: 1.0, z: 0.0)) 86 87 let linTime: Animation = .linear(duration:flipTime) 88 let frontAnimation = isFlipped ? linTime 89 : linTime.delay(flipTime) 90 let backAnimation = isFlipped ? linTime.delay(flipTime) 91 : linTime 92 let animatedImage = ZStack { 93 valueImage 94 .animation(flipBack ? nil : frontAnimation, value: isFlipped) 95 backImage 96 .animation(flipBack ? nil : backAnimation, value: isFlipped) 97 } 98 99 let shadow = (3 - 8) * pct + 8 100 return Button(action: action) { 101 animatedImage.opacity(invisible ? INVISIBLE 102 : unavailable ? 0.5 103 : 1) 104 } 105 .buttonStyle(OIMbuttonStyle()) 106 .disabled(disabled) 107 .frame(width: oWidth, height: oHeight) 108 .shadow(radius: shadow) 109 } 110 111 } 112 // MARK: - 113 /// renders 1 denomination from a currency 114 struct OIMcurrencyButton: View { 115 let stack: CallStack 116 let fund: OIMfund 117 let currency: OIMcurrency 118 let availableVal: UInt64 119 let canEdit: Bool 120 let isDrawer: Bool // debugging - button in the drawer 121 var pct: CGFloat 122 let action: () -> Void 123 124 func imgName(_ value: UInt64?) -> String? { 125 if let value { 126 return value > currency.bankCoins[0] ? currency.noteName(value) 127 : currency.coinName(value) 128 } 129 return nil 130 } 131 132 var body: some View { 133 let value = fund.value 134 let outValue = fund.outValue 135 let state = fund.state 136 let isMorphing = fund.morphTicker != nil 137 let flippedVal = fund.flippedVal 138 // Use EmptyView, because the modifier actually ignores 139 // the value passed to its body() function. 140 let isFlipped: Bool = switch state { 141 case .morphing: true 142 case .arriving: true 143 default: false 144 } 145 // if fund.id >= 0 { 146 // let _ = print("***Fund", fund.id, value, state, flippedVal) 147 // } 148 let mutating = state.mutating 149 let flipTime = mutating ? 0.0001 // flip back quickly from morphed 150 : fastAnimations ? 0.3 151 : 0.5 152 // checkDisabled = true // TODO: don't run twice 153 let unavailable = availableVal < value 154 if isDrawer && value == 2000 { 155 if unavailable { 156 let _ = print("OIMcurrencyButton❗️❗️", availableVal, value) 157 } 158 } 159 let disabled = !canEdit || isMorphing || unavailable // cannot edit morphing funds 160 EmptyView().modifier(OIMmod(name: imgName(outValue ?? value), 161 flippedName: imgName(flippedVal), 162 flipTime: flipTime, 163 isFlipped: isFlipped, 164 flipBack: mutating, 165 disabled: disabled, 166 unavailable: unavailable, 167 invisible: state.hiding || state.chestOpening, 168 pct: pct, 169 action: action)) 170 .accessibilityLabel(Text("\(value)", comment: "A11y")) // TODO: currency name 171 } 172 }