// // AddQRView.swift // OTP // // Created by Shadowfacts on 8/22/21. // import SwiftUI import CodeScanner import OTPKit struct AddQRView: View { let dismiss: (DismissAction) -> Void @State private var isPresentingScanFailedAlert = false @State private var scanError: ScanError? @State private var scannedKey: TOTPKey? @State private var isShowingConfirmView = false var body: some View { NavigationView { CodeScannerView(codeTypes: [.qr], scanMode: .once) { (result) in switch result { case .success(let code): if let components = URLComponents(string: code), let key = TOTPKey(urlComponents: components) { self.scannedKey = key isShowingConfirmView = true } else { isPresentingScanFailedAlert = true scanError = .invalidCode(code) } case .failure(let error): isPresentingScanFailedAlert = true scanError = .scanner(error) } } .edgesIgnoringSafeArea(.bottom) .navigationBarTitle("Scan QR Code", displayMode: .inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") { dismiss(.cancel) } } } .overlay { NavigationLink(isActive: $isShowingConfirmView) { EditKeyForm(editingKey: scannedKey, showCancelButton: false, dismiss: dismiss) .navigationTitle("Add Key") } label: { // EmptyView because this is only used to trigger programatic navigation EmptyView() } } } .alert("Unable to get OTP key from QR code", isPresented: $isPresentingScanFailedAlert) { Button("Cancel", role: .cancel) { dismiss(.cancel) } Button("Try Again") { isPresentingScanFailedAlert = false } } message: { if let error = scanError { Text(error.localizedDescription) } } } } extension AddQRView { enum ScanError: LocalizedError { case scanner(CodeScannerView.ScanError) case invalidCode(String) var errorDescription: String? { switch self { case .invalidCode(let code): return "Invalid Code: '\(code)'" case .scanner(.badInput): return "Scanner: Bad Input" case .scanner(.badOutput): return "Scanner: Bad Output" } } } } struct AddQRView_Previews: PreviewProvider { static var previews: some View { AddQRView() { (_) in } } }