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 }