IconBadge.swift (8068B)
1 /* 2 * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import SwiftUI 9 10 struct PendingIconBadge: View { 11 let foreColor:Color 12 let done: Bool 13 let incoming: Bool 14 let shouldConfirm: Bool 15 let needsKYC: Bool 16 17 var body: some View { 18 let image = incoming && done ? Image(systemName: DONE_INCOMING) // "plus.circle.fill" 19 : incoming ? Image(systemName: PENDING_INCOMING) // "plus" 20 // since outgoing money already left the wallet, show DONE_ and not PENDING_OUTGOING 21 : Image(systemName: DONE_OUTGOING) // "minus.circle" 22 IconBadge(image: image, 23 done: false, 24 foreColor: foreColor, 25 shouldConfirm: shouldConfirm, 26 needsKYC: needsKYC, 27 phase: 0, 28 firstImage: nil, 29 secondImage: nil, 30 wideIcon: nil) 31 } 32 } 33 // MARK: - 34 struct TransactionIconBadge: View { 35 var type: TransactionType 36 var foreColor: Color 37 let done: Bool 38 let incoming: Bool 39 let shouldConfirm: Bool 40 let needsKYC: Bool 41 let imageBase64: String? 42 43 @State private var image: Image? = nil 44 45 init(type: TransactionType, foreColor: Color, done: Bool, incoming: Bool, 46 shouldConfirm: Bool, needsKYC: Bool, imageBase64: String? = nil 47 ) { 48 self.type = type 49 self.foreColor = foreColor 50 self.done = done 51 self.incoming = incoming 52 self.shouldConfirm = shouldConfirm 53 self.needsKYC = needsKYC 54 self.imageBase64 = imageBase64 55 } 56 57 init(from transaction: TalerTransaction, isDark: Bool, _ increasedContrast: Bool, 58 imageBase64: String? = nil 59 ) { 60 let common = transaction.common 61 self.type = common.type 62 let done = transaction.isDone 63 self.done = done 64 let incoming = common.isIncoming 65 self.incoming = incoming 66 let needsKYC = transaction.isPendingKYC || transaction.isPendingKYCauth 67 self.needsKYC = needsKYC 68 self.shouldConfirm = transaction.shouldConfirm 69 let pending = transaction.isPending || transaction.common.isFinalizing 70 self.foreColor = .primary 71 72 let doneOrPending = done || pending 73 let isZero = common.amountEffective.isZero 74 let refreshZero = common.type.isRefresh && isZero 75 let textColor = doneOrPending ? .primary 76 : isDark ? .secondary 77 : increasedContrast ? Color(.darkGray) 78 : .secondary // Color(.tertiaryLabel) 79 let foreColor = refreshZero ? textColor 80 : pending ? WalletColors().pendingColor(incoming) 81 : done ? WalletColors().transactionColor(incoming) 82 : WalletColors().uncompletedColor 83 self.foreColor = foreColor 84 self.imageBase64 = imageBase64 85 } 86 87 var body: some View { 88 let badge = IconBadge(image: type.icon(done), 89 done: true, 90 foreColor: foreColor, 91 shouldConfirm: shouldConfirm, 92 needsKYC: needsKYC, 93 phase: 0, 94 firstImage: nil, 95 secondImage: nil, 96 wideIcon: TransactionType.refund.icon()) 97 // "arrowshape.turn.up.backward" is wider than all others 98 if let imageBase64 { 99 if let image { 100 image 101 .resizable() 102 .aspectRatio(contentMode: .fit) 103 .frame(maxHeight: 40) 104 } else { 105 badge 106 .opacity(0.3) 107 .task { 108 image = Image(imageBase64: imageBase64) 109 } 110 } 111 } else { 112 badge 113 } 114 } 115 } 116 // MARK: - 117 struct ButtonIconBadge: View { 118 let type: TransactionType 119 let phase: Int 120 let foreColor:Color 121 let done: Bool 122 123 var body: some View { 124 let isSend = type.isSendCoins 125 let isRcve = type.isSendInvoice 126 let isDepo = type.isDeposit 127 let isWthd = type.isWithdrawal 128 let left = ICONNAME_PERSON_LEFT + ICONNAME_FILL 129 let right = ICONNAME_PERSON_RIGHT 130 let bottom = ICONNAME_PERSON_BOTTOM + ICONNAME_FILL 131 let top = ICONNAME_BANK 132 let firstImage = isSend ? Image(left) 133 : isRcve ? Image(right) 134 : isDepo ? Image(bottom) 135 : isWthd ? Image(top) : nil 136 let secondImage = isSend ? Image(right) 137 : isRcve ? Image(left) 138 : isDepo ? Image(top) 139 : isWthd ? Image(bottom) : nil 140 IconBadge(image: type.icon(done), 141 done: true, 142 foreColor: foreColor, 143 shouldConfirm: false, 144 needsKYC: false, 145 phase: phase, 146 firstImage: firstImage, 147 secondImage: secondImage, 148 wideIcon: TransactionType.peerPushDebit.icon()) 149 // button is send/receive/withdraw/deposit, never payment or refund 150 } 151 } 152 // MARK: - 153 struct IconBadge: View { 154 let image: Image 155 let done: Bool 156 let foreColor:Color 157 let shouldConfirm: Bool 158 let needsKYC: Bool 159 let phase: Int 160 let firstImage: Image? 161 let secondImage: Image? 162 let wideIcon: Image? // cheating: ZStack with widest icon to ensure all have the same width 163 // TODO: EqualIconWidth... 164 165 @ScaledMetric var spacing = 6 // relative to fontSize 166 @State private var showFirst = false 167 @State private var showSecond = false 168 @State private var showImage = true 169 170 var body: some View { 171 // let _ = Self._printChanges() 172 HStack(alignment: .top, spacing: -spacing) { 173 ZStack { 174 if let wideIcon { 175 wideIcon.foregroundColor(.clear) 176 } 177 if let firstImage, let secondImage { 178 let duration = 0.66 179 ZStack { 180 image.opacity(showImage ? 1 : 0) 181 .animation(.easeInOut(duration: duration), value: showImage) 182 firstImage.opacity(showFirst ? 1 : 0) 183 .animation(.easeOut(duration: duration), value: showFirst) 184 secondImage.opacity(showSecond ? 1 : 0) 185 .animation(.easeIn(duration: duration), value: showSecond) 186 } .foregroundColor(foreColor) 187 .onChange(of: phase) { phaseVal in 188 switch phaseVal { 189 case 0, 4: showFirst = false; showSecond = false; showImage = true 190 case 2, 3: showFirst = true; showSecond = false; showImage = false 191 case 5, 6: showFirst = false; showSecond = true; showImage = false 192 default: showFirst = false; showSecond = false; showImage = false 193 } 194 } 195 } else { 196 image.foregroundColor(foreColor) 197 } 198 } 199 // ZStack centers the main icon, so the badge will always be at the same position 200 let badgeName = needsKYC ? NEEDS_KYC 201 : CONFIRM_BANK 202 Image(systemName: badgeName) 203 .talerFont(.badge) 204 .foregroundColor(needsKYC ? WalletColors().attention 205 : shouldConfirm ? WalletColors().confirm 206 : .clear) 207 .padding(.top, -2) 208 }.accessibilityHidden(true) 209 } 210 } 211 // MARK: - 212 //#Preview { 213 // IconBadge() 214 //}