taler-ios

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

IconBadge.swift (7258B)


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