taler-ios

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

TransactionsEmptyView.swift (7118B)


      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 /// This view shows hints if a wallet is empty
     12 /// It is the very first thing the user sees after installing the app
     13 
     14 struct TransactionsEmptyView: View {
     15     let stack: CallStack
     16     let currency: String
     17 
     18     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     19 
     20     var body: some View {
     21         List {
     22             Section {
     23                 Text("There are no transactions for \(currency).")
     24             }
     25             .talerFont(.title2)
     26         }
     27         .listStyle(myListStyle.style).anyView
     28         .background(FullBackground())
     29         .onAppear() {
     30             DebugViewC.shared.setViewID(VIEW_EMPTY_HISTORY, stack: stack.push())     // 20
     31         }
     32     }
     33 }
     34 
     35 // MARK: -
     36 struct UnconfirmedKeyChangeView: View {
     37     var baseURL: String
     38     var balance: Balance
     39     var isLegacy: Bool
     40 
     41     @Environment(\.dismiss) var dismiss     // pop back once
     42     @EnvironmentObject private var model: WalletModel
     43     @EnvironmentObject private var controller: Controller
     44     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     45 
     46     @State private var exchange: Exchange? = nil
     47     @State private var alertAccept: Bool = false
     48     @State private var alertDelete: Bool = false
     49 
     50     func notValid(_ amountString: String) -> String {
     51         String(localized: "Unfortunately this means, that your \(amountString) are not valid anymore, because they were obtained with the old security identity.")
     52     }
     53 
     54     func keep(_ amountString: String) -> String {
     55         String(localized: "Keep the \(amountString)")
     56     }
     57 
     58     func delete(_ amountString: String) -> String {
     59         String(localized: "Delete the \(amountString)")
     60     }
     61 
     62     func acceptAction() {
     63         if let exchange, let masterPub = exchange.unconfirmedKeyChange?.currentMasterPub ?? exchange.masterPub {
     64             Task {
     65                 try? await model.confirmExchangeKeyChange(exchangeBaseUrl: baseURL,
     66                                                          currentMasterPub: masterPub)
     67                 dismiss()
     68             }
     69         } else {
     70             // TODO: Yikes!
     71         }
     72     }
     73 
     74     func deleteAction() {
     75         if let exchange, let masterPub = exchange.masterPub {
     76             Task {
     77                 try? await model.purgeExchangeLegacyKeys(exchangeBaseUrl: baseURL,
     78                                                         currentMasterPub: masterPub)
     79                 dismiss()
     80             }
     81         } else {
     82             // TODO: Yikes!
     83         }
     84     }
     85 
     86     func keepAction() {
     87         withAnimation {
     88             dismiss()
     89         }
     90     }
     91 
     92     private var acceptButton: some View {
     93         Button("Accept") {
     94             alertAccept = false
     95             acceptAction()
     96         }
     97     }
     98     private var deleteButton: some View {
     99         Button("Delete") {
    100             alertDelete = false
    101             deleteAction()
    102         }
    103     }
    104     private var dismissAlertButton: some View {
    105         Button("Cancel", role: .cancel) {
    106             alertAccept = false
    107             alertDelete = false
    108         }
    109     }
    110 
    111     @MainActor
    112     private func viewDidLoad() async {
    113         // confirm and purge must send the master key wallet-core uses *now*
    114         // (not the controller's cached entry) or they fail with KEY_CHANGE_MISMATCH / BAD_REQUEST
    115         if let fresh = try? await model.getExchangeByUrl(url: baseURL, viewHandles: true) {
    116             exchange = fresh
    117         } else {
    118             exchange = await controller.exchange(for: baseURL)
    119         }
    120     }
    121 
    122     var body: some View {
    123         let scope = balance.scopeInfo
    124         let available = balance.available
    125         let formatted = available.formatted(scope, isNegative: false)
    126         let navTitle = String(localized: "Security Alert!", comment: "ViewTitle of UnconfirmedKeyChangeView")
    127         let warning1 = Text("Only trust the new identity if the payment service has confirmed this change.")
    128         let warning2 = Text("Otherwise, wait and try again later...")
    129         List {
    130             Section {
    131                 Text("The security identity of \(baseURL.trimURL) has changed unexpectedly.")
    132 
    133                 if isLegacy {
    134                     if !available.isZero {
    135                         let sorry = notValid(formatted.0)
    136                         let a11y = notValid(formatted.1)
    137                         Text(sorry)
    138                             .accessibilityLabel(a11y)
    139                         
    140                         warning1
    141                             .talerFont(.body)
    142                         warning2
    143                             .talerFont(.headline)
    144 
    145                         Button(keep(formatted.0), action: keepAction)
    146                             .buttonStyle(TalerButtonStyle(type: .bordered))
    147                             .padding(.horizontal)
    148                             .accessibilityLabel(keep(formatted.1))
    149                         
    150                         Text("If you are sure that the old security identity will not be used anymore, you can delete the invalid money.")
    151                             .talerFont(.body)
    152                         Button(delete(formatted.0), action: { alertDelete = true })
    153                             .buttonStyle(TalerButtonStyle(type: .bordered))
    154                             .padding(.horizontal)
    155                             .accessibilityLabel(delete(formatted.1))
    156                     }
    157                 } else {
    158                     warning1
    159                     warning2
    160                         .bold()
    161                     Text("If you are sure that the old security identity will not be used anymore, you can withdraw new money with the new security identity.")
    162                     Button("Accept new security identity", action: { alertAccept = true })
    163                         .buttonStyle(TalerButtonStyle(type: .bordered))
    164                         .padding(.horizontal)
    165                     Text("Money you obtained with the old security identity will be invalid.")
    166                 }
    167             }
    168             .talerFont(.title2)
    169             .listRowSeparator(.hidden)
    170         }
    171         .listStyle(myListStyle.style).anyView
    172         .background(FullBackground())
    173         .navigationTitle(navTitle)
    174         .task {
    175             await viewDidLoad()
    176         }
    177         .alert("Accept the new payment service security identity.",
    178                isPresented: $alertAccept,
    179                actions: {   acceptButton
    180                             dismissAlertButton },
    181                message: {   Text("Are you sure? Your money obtained with the old security identity will be invalid.") })
    182         .alert("Delete money obtained with the old security identity.",
    183                isPresented: $alertDelete,
    184                actions: {   deleteButton
    185                             dismissAlertButton },
    186                message: {   Text("Are you sure? That money will be lost.") })
    187     }
    188 }
    189 // MARK: -
    190 struct TransactionsEmptyView_Previews: PreviewProvider {
    191     static var previews: some View {
    192         TransactionsEmptyView(stack: CallStack("Preview"), currency: LONGCURRENCY)
    193     }
    194 }