taler-ios

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

ErrorView.swift (5227B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-26 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     let stack: CallStack
     18     var title: String
     19     var message: String? = nil
     20     var copyable: Bool
     21 
     22     var onDismiss: () -> Void
     23 
     24     init(_ stack: CallStack, title: String, message: String? = nil, copyable: Bool, onDismiss: @escaping () -> Void) {
     25         self.stack = stack
     26         self.title = title
     27         self.message = message
     28         self.copyable = copyable
     29         self.onDismiss = onDismiss
     30     }
     31 
     32     init(_ stack: CallStack, error: Error, devMode: Bool, onDismiss: @escaping () -> Void) {
     33         let internetError = String(localized: "Network problem")
     34         let internetMessage = String(localized: "Please check your internet connection and try again.")
     35         let walletCoreError = String(localized: "Internal core error")
     36         let initializationError = String(localized: "Initialization error")
     37         let serializationError = String(localized: "Serialization error")
     38         let deserializationError = String(localized: "Deserialization error")
     39         let unknownError = String(localized: "Unknown error")
     40 
     41         switch error {
     42             case let walletError as WalletBackendError:
     43                 switch walletError {
     44                     case .walletCoreError(let walletBackendResponseError):
     45                         if let responseError = walletBackendResponseError {
     46                             if responseError.code == 7001 {
     47                                 self.init(stack, title: internetError, message: internetMessage, copyable: false, onDismiss: onDismiss)
     48                                 break
     49                             }
     50                         }
     51                         if let json = walletBackendResponseError?.toJSON(), devMode {
     52                             self.init(stack, title: walletCoreError, message: json, copyable: true, onDismiss: onDismiss)
     53                         } else if let hint = walletBackendResponseError?.errorResponse?.hint ?? walletBackendResponseError?.hint {
     54                             self.init(stack, title: walletCoreError, message: hint, copyable: false, onDismiss: onDismiss)
     55                         } else if let message = walletBackendResponseError?.message {
     56                             self.init(stack, title: walletCoreError, message: message, copyable: false, onDismiss: onDismiss)
     57                         } else {
     58                             self.init(stack, title: walletCoreError, copyable: false, onDismiss: onDismiss)
     59                         }
     60                     case .initializationError:
     61                         self.init(stack, title: initializationError, copyable: false, onDismiss: onDismiss)
     62                     case .serializationError:
     63                         self.init(stack, title: serializationError, copyable: false, onDismiss: onDismiss)
     64                     case .deserializationError:
     65                         self.init(stack, title: deserializationError, copyable: false, onDismiss: onDismiss)
     66                 }
     67             default:
     68                 self.init(stack, title: unknownError, message: error.localizedDescription, copyable: false, onDismiss: onDismiss)
     69         }
     70     }
     71 
     72     init(_ stack: CallStack, data: ErrorData, devMode: Bool, onDismiss: @escaping () -> Void) {
     73         switch data {
     74             case .message(let title, let message):
     75                 self.init(stack, title: title, message: message, copyable: false, onDismiss: onDismiss)
     76             case .error(let error):
     77                 self.init(stack, error: error, devMode: devMode, onDismiss: onDismiss)
     78         }
     79     }
     80 
     81     var body: some View {
     82         ScrollView {
     83             VStack {
     84                 Image(systemName: EXCLAMATION)
     85                     .resizable()
     86                     .frame(width: 50, height: 50)
     87                     .aspectRatio(contentMode: .fit)
     88                     .foregroundStyle(WalletColors().attention)
     89                     .padding()
     90 
     91                 Text(title)
     92                     .talerFont(.title)
     93                     .padding(.bottom)
     94 
     95                 if let message {
     96                     if copyable {
     97                         if #available(iOS 16.4, *) {
     98                             Text(message).monospaced()
     99                         } else {
    100                             Text(message).font(.system(.body, design: .monospaced))
    101                         }
    102 
    103                         CopyButton(textToCopy: message, vertical: false)
    104                             .accessibilityLabel(Text("Copy the error JSON", comment: "a11y"))
    105                             .padding()
    106                     } else {
    107                         Text(message)
    108                             .multilineTextAlignment(.center)
    109                     }
    110                 }
    111             }
    112         }
    113         .padding()
    114         .safeAreaInset(edge: .bottom) {
    115             Button("Close", role: .cancel) {
    116                 onDismiss()
    117             }
    118             .buttonStyle(TalerButtonStyle(type: .bordered))
    119             .padding(.bottom)
    120             .padding(.horizontal)
    121         }
    122     }
    123 }