taler-ios

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

ErrorView.swift (5906B)


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