taler-ios

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

IconBadge.swift (7723B)


      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 
     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     init(type: TransactionType, foreColor: Color, done: Bool, incoming: Bool,
     44          shouldConfirm: Bool, needsKYC: Bool, imageBase64: String? = nil
     45     ) {
     46         self.type = type
     47         self.foreColor = foreColor
     48         self.done = done
     49         self.incoming = incoming
     50         self.shouldConfirm = shouldConfirm
     51         self.needsKYC = needsKYC
     52         self.imageBase64 = imageBase64
     53     }
     54 
     55     init(from transaction: TalerTransaction, isDark: Bool, _ increasedContrast: Bool,
     56          imageBase64: String? = nil
     57     ) {
     58         let common = transaction.common
     59         self.type = common.type
     60         let done = transaction.isDone
     61         self.done = done
     62         let incoming = common.isIncoming
     63         self.incoming = incoming
     64         let needsKYC = transaction.isPendingKYC || transaction.isPendingKYCauth
     65         self.needsKYC = needsKYC
     66         self.shouldConfirm = transaction.shouldConfirm
     67         let pending = transaction.isPending || transaction.common.isFinalizing
     68         self.foreColor = .primary
     69 
     70         let doneOrPending = done || pending
     71         let isZero = common.amountEffective.isZero
     72         let refreshZero = common.type.isRefresh && isZero
     73         let textColor = doneOrPending ? .primary
     74                              : isDark ? .secondary
     75                   : increasedContrast ? Color(.darkGray)
     76                                       : .secondary  // Color(.tertiaryLabel)
     77         let foreColor = refreshZero ? textColor
     78                           : pending ? WalletColors().pendingColor(incoming)
     79                              : done ? WalletColors().transactionColor(incoming)
     80                                     : WalletColors().uncompletedColor
     81         self.foreColor = foreColor
     82         self.imageBase64 = imageBase64
     83     }
     84 
     85     var body: some View {
     86         if let imageBase64, let image = Image(imageBase64: imageBase64) {
     87             image
     88                 .resizable()
     89                 .aspectRatio(contentMode: .fit)
     90                 .frame(maxHeight: 40)
     91         } else {
     92             IconBadge(image: type.icon(done),
     93                        done: true,
     94                   foreColor: foreColor,
     95               shouldConfirm: shouldConfirm,
     96                    needsKYC: needsKYC,
     97                       phase: 0,
     98                  firstImage: nil,
     99                 secondImage: nil,
    100                    wideIcon: TransactionType.refund.icon())
    101                           // "arrowshape.turn.up.backward" is wider than all others
    102         }
    103     }
    104 }
    105 // MARK: -
    106 struct ButtonIconBadge: View {
    107     let type: TransactionType
    108     let phase: Int
    109     let foreColor:Color
    110     let done: Bool
    111 
    112     var body: some View {
    113         let isSend = type.isSendCoins
    114         let isRcve = type.isSendInvoice
    115         let isDepo = type.isDeposit
    116         let isWthd = type.isWithdrawal
    117         let left = ICONNAME_PERSON_LEFT + ICONNAME_FILL
    118         let right = ICONNAME_PERSON_RIGHT
    119         let bottom = ICONNAME_PERSON_BOTTOM + ICONNAME_FILL
    120         let top = ICONNAME_BANK
    121         let firstImage = isSend ? Image(left)
    122                        : isRcve ? Image(right)
    123                        : isDepo ? Image(bottom)
    124                        : isWthd ? Image(top) : nil
    125         let secondImage = isSend ? Image(right)
    126                         : isRcve ? Image(left)
    127                         : isDepo ? Image(top)
    128                         : isWthd ? Image(bottom) : nil
    129         IconBadge(image: type.icon(done),
    130                    done: true,
    131               foreColor: foreColor,
    132           shouldConfirm: false,
    133                needsKYC: false,
    134                   phase: phase,
    135              firstImage: firstImage,
    136             secondImage: secondImage,
    137                wideIcon: TransactionType.peerPushDebit.icon())
    138                       // button is send/receive/withdraw/deposit, never payment or refund
    139     }
    140 }
    141 // MARK: -
    142 struct IconBadge: View {
    143     let image: Image
    144     let done: Bool
    145     let foreColor:Color
    146     let shouldConfirm: Bool
    147     let needsKYC: Bool
    148     let phase: Int
    149     let firstImage: Image?
    150     let secondImage: Image?
    151     let wideIcon: Image?        // cheating: ZStack with widest icon to ensure all have the same width
    152                                 // TODO: EqualIconWidth...
    153 
    154     @ScaledMetric var spacing = 6       // relative to fontSize
    155     @State private var showFirst = false
    156     @State private var showSecond = false
    157     @State private var showImage = true
    158 
    159     var body: some View {
    160 //        let _ = Self._printChanges()
    161         HStack(alignment: .top, spacing: -spacing) {
    162             ZStack {
    163                 if let wideIcon {
    164                     wideIcon.foregroundColor(.clear)
    165                 }
    166                 if let firstImage, let secondImage {
    167                     let duration = 0.66
    168                     ZStack {
    169                         image.opacity(showImage ? 1 : 0)
    170                             .animation(.easeInOut(duration: duration), value: showImage)
    171                         firstImage.opacity(showFirst ? 1 : 0)
    172                             .animation(.easeOut(duration: duration), value: showFirst)
    173                         secondImage.opacity(showSecond ? 1 : 0)
    174                             .animation(.easeIn(duration: duration), value: showSecond)
    175                     }   .foregroundColor(foreColor)
    176                         .onChange(of: phase) { phaseVal in
    177                             switch phaseVal {
    178                                 case 0, 4: showFirst = false; showSecond = false; showImage = true
    179                                 case 2, 3: showFirst = true; showSecond = false; showImage = false
    180                                 case 5, 6: showFirst = false; showSecond = true; showImage = false
    181                                 default: showFirst = false; showSecond = false; showImage = false
    182                             }
    183                         }
    184                 } else {
    185                     image.foregroundColor(foreColor)
    186                 }
    187             }
    188             // ZStack centers the main icon, so the badge will always be at the same position
    189             let badgeName = needsKYC ? NEEDS_KYC
    190                                      : CONFIRM_BANK
    191             Image(systemName: badgeName)
    192                 .talerFont(.badge)
    193                 .foregroundColor(needsKYC ? WalletColors().attention
    194                           : shouldConfirm ? WalletColors().confirm
    195                                           : .clear)
    196                 .padding(.top, -2)
    197         }.accessibilityHidden(true)
    198     }
    199 }
    200 // MARK: -
    201 //#Preview {
    202 //    IconBadge()
    203 //}