taler-ios

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

ErrorView.swift (4761B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-25 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Iván Ávalos
      7  */
      8 import SwiftUI
      9 import taler_swift
     10 
     11 enum ErrorData {
     12     case message(title: String, message: String)
     13     case error(Error)
     14 }
     15 
     16 struct ErrorView: View {
     17     var title: String
     18     var message: String? = nil
     19     var copyable: Bool
     20 
     21     var onDismiss: () -> Void
     22 
     23     init(title: String, message: String? = nil, copyable: Bool, onDismiss: @escaping () -> Void) {
     24         self.title = title
     25         self.message = message
     26         self.copyable = copyable
     27         self.onDismiss = onDismiss
     28     }
     29 
     30     init(error: Error, devMode: Bool, onDismiss: @escaping () -> Void) {
     31         let walletCoreError = String(localized: "Internal core error")
     32         let initializationError = String(localized: "Initialization error")
     33         let serializationError = String(localized: "Serialization error")
     34         let deserializationError = String(localized: "Deserialization error")
     35         let unknownError = String(localized: "Unknown error")
     36 
     37         switch error {
     38         case let walletError as WalletBackendError:
     39             switch walletError {
     40             case .walletCoreError(let error):
     41                 if let json = error?.toJSON(), devMode {
     42                     self.init(title: walletCoreError, message: json, copyable: true, onDismiss: onDismiss)
     43                 } else if let hint = error?.errorResponse?.hint ?? error?.hint {
     44                     self.init(title: walletCoreError, message: hint, copyable: false, onDismiss: onDismiss)
     45                 } else if let message = error?.message {
     46                     self.init(title: walletCoreError, message: message, copyable: false, onDismiss: onDismiss)
     47                 } else {
     48                     self.init(title: walletCoreError, copyable: false, onDismiss: onDismiss)
     49                 }
     50             case .initializationError:
     51                 self.init(title: initializationError, copyable: false, onDismiss: onDismiss)
     52             case .serializationError:
     53                 self.init(title: serializationError, copyable: false, onDismiss: onDismiss)
     54             case .deserializationError:
     55                 self.init(title: deserializationError, copyable: false, onDismiss: onDismiss)
     56             }
     57         default:
     58             self.init(title: unknownError, message: error.localizedDescription, copyable: false, onDismiss: onDismiss)
     59         }
     60     }
     61 
     62     init(data: ErrorData, devMode: Bool, onDismiss: @escaping () -> Void) {
     63         switch data {
     64             case .message(let title, let message):
     65                 self.init(title: title, message: message, copyable: false, onDismiss: onDismiss)
     66             case .error(let error):
     67                 self.init(error: error, devMode: devMode, onDismiss: onDismiss)
     68         }
     69     }
     70 
     71     var body: some View {
     72         ScrollView {
     73             VStack {
     74                 Image(systemName: "exclamationmark.circle")
     75                     .resizable()
     76                     .frame(width: 50, height: 50)
     77                     .aspectRatio(contentMode: .fit)
     78                     .foregroundStyle(WalletColors().attention)
     79                     .padding()
     80 
     81                 Text(title)
     82                     .talerFont(.title)
     83                     .padding(.bottom)
     84 
     85                 if let message {
     86                     if copyable {
     87                         if #available(iOS 16.4, *) {
     88                             Text(message).monospaced()
     89                         } else {
     90                             Text(message).font(.system(.body, design: .monospaced))
     91                         }
     92 
     93                         CopyButton(textToCopy: message, vertical: false)
     94                             .accessibilityLabel(Text("Copy the error JSON", comment: "a11y"))
     95                             .padding()
     96                     } else {
     97                         Text(message)
     98                             .multilineTextAlignment(.center)
     99                     }
    100                 }
    101             }
    102         }
    103         .padding()
    104         .safeAreaInset(edge: .bottom) {
    105             Button("Close", role: .cancel) {
    106                 onDismiss()
    107             }
    108             .buttonStyle(TalerButtonStyle(type: .bordered))
    109             .padding(.bottom)
    110             .padding(.horizontal)
    111         }
    112     }
    113 }
    114 
    115 struct ErrorSheet_Previews: PreviewProvider {
    116     static let error = WalletBackendError.walletCoreError(WalletBackendResponseError(
    117         code: 7025, when: Timestamp.now(),
    118         hint: "A KYC step is required before withdrawal can proceed",
    119         message: "A KYC step is required before withdrawal can proceed"))
    120 
    121     static var previews: some View {
    122         ErrorView(error: error, devMode: true, onDismiss: {})
    123         ErrorView(error: error, devMode: false, onDismiss: {})
    124     }
    125 }