taler-ios

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

BarGraph.swift (4746B)


      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 SymLog
     10 
     11 let MAXBARS = 15
     12 
     13 struct BarGraphHeader: View {
     14     private let symLog = SymLogV(0)
     15     let stack: CallStack
     16     let scope: ScopeInfo
     17     @Binding var reloadTransactions: Int
     18 
     19     @EnvironmentObject private var model: WalletModel
     20     @EnvironmentObject private var controller: Controller
     21     @Environment(\.colorScheme) private var colorScheme
     22     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     23     @AppStorage("minimalistic") var minimalistic: Bool = false
     24 
     25     @State private var completedTransactions: [TalerTransaction] = []
     26     @ScaledMetric var barHeight = 9       // relative to fontSize
     27 
     28     @MainActor
     29     private func loadCompleted() async {
     30         symLog.log(".task for BarGraphHeader(\(scope.currency)) - load \(MAXBARS) Transactions")
     31         if let response = try? await model.getTransactionsV2(stack.push("BarGraphHeader - \(scope.url?.trimURL)"),
     32                                                       scope: scope,
     33                                               filterByState: .done,
     34                                                       limit: MAXBARS
     35         ) {
     36             completedTransactions = response
     37         }
     38     }
     39 
     40     var body: some View {
     41         let currencyInfo = controller.info(for: scope, controller.currencyTicker)
     42         HStack (alignment: .center, spacing: 10) {
     43             if !minimalistic || currencyInfo.hasSymbol {
     44                 Text(currencyInfo.name)
     45                     .talerFont(.title2)
     46                     .foregroundColor(WalletColors().secondary(colorScheme, colorSchemeContrast))
     47             }
     48             BarGraph(transactions: $completedTransactions,
     49                           maxBars: MAXBARS, barHeight: barHeight)
     50         }
     51 //        .headerProminence(.increased)         // unfortunately this is not useful
     52         .task(id: reloadTransactions + 2_000_000) { await loadCompleted() }
     53     }
     54 }
     55 // MARK: -
     56 struct BarGraph: View {
     57     @Binding var transactions: [TalerTransaction]
     58     let maxBars: Int
     59     let barHeight: Double
     60 
     61     func maxValue(_ someTransactions: [TalerTransaction]) -> Double {
     62         var maxValue = 0.0
     63         for transaction in someTransactions {
     64             let value = transaction.common.amountEffective.value
     65             if value > maxValue {
     66                 maxValue = value
     67             }
     68         }
     69         return maxValue
     70     }
     71 
     72     var body: some View {
     73         let slice = transactions.prefix(maxBars)
     74         let count = slice.count
     75         let tenTransactions: [TalerTransaction] = Array(slice)
     76         let maxValue = maxValue(tenTransactions)
     77 
     78         HStack(alignment: .center, spacing: 1) {
     79             if count > 0 {
     80                 ForEach(tenTransactions, id: \.self) {transaction in
     81                     let common = transaction.common
     82                     let incoming = common.isIncoming
     83                     let netto = common.amountEffective.value
     84                     let valueColored = barHeight * netto / maxValue
     85                     let valueTransparent = barHeight - valueColored
     86 //                    let _ = print("max: \(maxValue), ", incoming ? "+" : "-", netto)
     87                     VStack(spacing: 0) {
     88                         let width = barHeight / 3
     89                         let topHeight = incoming ? valueTransparent : barHeight
     90                         let botHeight = incoming ? barHeight : valueTransparent
     91                         if topHeight > 0 {
     92                             Rectangle()
     93                                 .opacity(INVISIBLE)
     94                                 .frame(width: width, height: topHeight)
     95                         }
     96                         Rectangle()
     97                             .foregroundColor(incoming ? WalletColors().positive : WalletColors().negative)
     98                             .frame(width: width, height: valueColored)
     99                         if botHeight > 0 {
    100                             Rectangle()
    101                                 .opacity(INVISIBLE)
    102                                 .frame(width: width, height: botHeight)
    103                         }
    104                     }
    105                 }
    106             }
    107         }
    108         .accessibilityHidden(true)      // cannot speak out this bar chart info
    109         .flippedDirection()             // draw first array item on trailing edge
    110     }
    111 }
    112 
    113 
    114 
    115 #if false
    116 #Preview {
    117     var sampleBars: [BarData] {
    118         var tempBars = [BarData]()
    119 
    120         for _ in 1...8 {
    121             let rand = Double.random(in: -100.0...100.0)
    122 
    123             let bar = BarData(value: rand)
    124             tempBars.append(bar)
    125         }
    126         return tempBars
    127     }
    128 
    129     return BarGraph(bars: sampleBars)
    130 }
    131 #endif