diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index e3f7b6dbe..2fc4394b3 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -102,14 +102,10 @@ private actor Service { variant: .curve25519, userIds: [userId] ) - try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) - try appContext.encryptedStorage.putKeypairs( - keyDetails: [encryptedPrv.key], - passPhrase: storageMethod == .persistent ? passPhrase: nil, - source: .generated, - for: user.email - ) + try await submitKeyToAttester(email: userId.email, publicKey: encryptedPrv.key.public) + try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) + try await putKeypairsInEncryptedStorage(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) if storageMethod == .memory { let passPhrase = PassPhrase( @@ -119,12 +115,6 @@ private actor Service { try appContext.passPhraseService.savePassPhrase(with: passPhrase, storageMethod: .memory) } - await submitKeyToAttesterAndShowAlertOnFailure( - email: userId.email, - publicKey: encryptedPrv.key.public, - viewController: viewController - ) - // sending welcome email is not crucial, so we don't handle errors _ = try? await attester.testWelcome( email: userId.email, @@ -132,11 +122,20 @@ private actor Service { ) } - private func submitKeyToAttesterAndShowAlertOnFailure( + @MainActor + private func putKeypairsInEncryptedStorage(encryptedPrv: CoreRes.GenerateKey, storageMethod: StorageMethod, passPhrase: String) throws { + try appContext.encryptedStorage.putKeypairs( + keyDetails: [encryptedPrv.key], + passPhrase: storageMethod == .persistent ? passPhrase: nil, + source: .generated, + for: user.email + ) + } + + private func submitKeyToAttester( email: String, - publicKey: String, - viewController: ViewController - ) async { + publicKey: String + ) async throws { do { _ = try await attester.update( email: email, @@ -144,17 +143,16 @@ private actor Service { token: appContext.dataService.token ) } catch { - let message = "Failed to submit Public Key" - await viewController.showAlert(error: error, message: message) + throw CreateKeyError.submitKey(error) } } private func getUserId() throws -> UserId { guard let email = appContext.dataService.email, !email.isEmpty else { - throw CreateKeyError.missedUserEmail + throw CreateKeyError.missingUserEmail } guard let name = appContext.dataService.currentUser?.name, !name.isEmpty else { - throw CreateKeyError.missedUserName + throw CreateKeyError.missingUserName } return UserId(email: email, name: name) } diff --git a/FlowCrypt/Functionality/DataManager/DataService.swift b/FlowCrypt/Functionality/DataManager/DataService.swift index 485ae8ee7..1239a5fec 100644 --- a/FlowCrypt/Functionality/DataManager/DataService.swift +++ b/FlowCrypt/Functionality/DataManager/DataService.swift @@ -104,7 +104,10 @@ extension DataService: DataServiceType { appDelegateGoogleSessionContainer: nil // needed only when signing in/out ).accessToken default: - return nil + guard let currentUser = currentUser else { + return nil + } + return Imap(user: currentUser).imapSess?.oAuth2Token } } diff --git a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift index aa7cbcc17..ea0988f16 100644 --- a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift @@ -35,7 +35,6 @@ private struct ComposedErrorHandler: ErrorHandler { handlers: [ KeyServiceErrorHandler(), BackupServiceErrorHandler(), - CreateKeyErrorHandler() ] ) diff --git a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift index 059befeab..3f3a8fef6 100644 --- a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift @@ -10,14 +10,37 @@ import UIKit enum CreateKeyError: Error { case weakPassPhrase(_ strength: CoreRes.ZxcvbnStrengthBar) - // Missing user email - case missedUserEmail - // Missing user name - case missedUserName - // Pass phrases don't match + /// Missing user email + case missingUserEmail + /// Missing user name + case missingUserName + /// Pass phrases don't match case doesntMatch - // silent abort + /// Silent abort case conformingPassPhraseError + /// Failed to submit key + case submitKey(Error) +} + +extension CreateKeyError: CustomStringConvertible { + var description: String { + switch self { + case .weakPassPhrase(let strength): + return "Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase." + case .missingUserEmail: + return "backupServiceError_email".localized + case .missingUserName: + return "backupServiceError_name".localized + case .doesntMatch: + return "pass_phrase_match_error".localized + case .submitKey(let error): + return "submit_key_error".localized + + "\n" + + "\(error.errorMessage)" + case .conformingPassPhraseError: + return "" + } + } } // KeyServiceError @@ -44,31 +67,3 @@ struct KeyServiceErrorHandler: ErrorHandler { return true } } - -// CreateKeyError -struct CreateKeyErrorHandler: ErrorHandler { - func handle(error: Error, for viewController: UIViewController) -> Bool { - let errorMessage: String? - - switch error as? CreateKeyError { - case .weakPassPhrase(let strength): - errorMessage = "Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase." - case .missedUserEmail: - errorMessage = "backupServiceError_email".localized - case .missedUserName: - errorMessage = "backupServiceError_name".localized - case .doesntMatch: - errorMessage = "pass_phrase_match_error".localized - case .conformingPassPhraseError: - errorMessage = nil - case .none: - errorMessage = nil - } - - guard let message = errorMessage else { return false } - - viewController.showAlert(message: message) - - return true - } -} diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift index 0f0fe2672..6ab946d12 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift @@ -6,9 +6,9 @@ import Foundation extension Imap: MessageGateway { func sendMail(input: MessageGatewayInput, progressHandler: ((Float) -> Void)?) async throws { - try await withCheckedThrowingContinuation { [weak smtpSess] (continuation: CheckedContinuation) in - guard let session = smtpSess else { - return continuation.resume(returning: ()) + try await withCheckedThrowingContinuation { [weak self] (continuation: CheckedContinuation) in + guard let session = self?.smtpSess else { + return continuation.resume(throwing: ImapError.noSession) } session.sendOperation(with: input.mime) diff --git a/FlowCrypt/Functionality/Services/ApiCall.swift b/FlowCrypt/Functionality/Services/ApiCall.swift index d53d93b14..c87d732fa 100644 --- a/FlowCrypt/Functionality/Services/ApiCall.swift +++ b/FlowCrypt/Functionality/Services/ApiCall.swift @@ -65,16 +65,24 @@ struct ApiError: LocalizedError { extension ApiError { static func create(from httpError: HttpErr, request: ApiCall.Request) -> Self { - guard - let data = httpError.data, - let object = try? JSONDecoder().decode(HttpError.self, from: data) - else { + guard let data = httpError.data else { return ApiError( errorDescription: httpError.error?.localizedDescription ?? "", internalError: httpError.error ) } + guard let object = try? JSONDecoder().decode(HttpError.self, from: data) else { + let errorDescription = httpError.error?.localizedDescription + ?? String(data: data, encoding: .utf8) + ?? "missing error description" + + return ApiError( + errorDescription: errorDescription, + internalError: httpError.error + ) + } + var message = "\(request.apiName) \(object.code) \(object.message)" message += "\n" message += "\(request.method) \(request.url)" diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index e193c638a..8d31395ba 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -37,6 +37,7 @@ extension BackupService: BackupServiceType { } } + @MainActor func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws { let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) diff --git a/FlowCrypt/Resources/en.lproj/Localizable.strings b/FlowCrypt/Resources/en.lproj/Localizable.strings index 22f6bf49a..53f27ab1b 100644 --- a/FlowCrypt/Resources/en.lproj/Localizable.strings +++ b/FlowCrypt/Resources/en.lproj/Localizable.strings @@ -298,3 +298,5 @@ "imap_error_no_session" = "Session isn't established"; "imap_error_provider" = "Provider error %@"; "imap_error_msg_info" = "Can't parse message without \"%@\""; + +"submit_key_error" = "Failed to submit Public Key"; diff --git a/FlowCryptAppTests/ExtensionTests.swift b/FlowCryptAppTests/ExtensionTests.swift index 667482586..87d9c0f89 100644 --- a/FlowCryptAppTests/ExtensionTests.swift +++ b/FlowCryptAppTests/ExtensionTests.swift @@ -88,13 +88,20 @@ extension ExtensionTests { timeZone: .current, year: year, month: 1, - day: 24 + day: 24, + hour: 18, + minute: 34, + second: 9 ).date) // Jan 24, 2020 let otherYearDate = Date(timeIntervalSince1970: 1579883652) XCTAssertTrue(dateFormatter.date(from: DateFormatter().formatDate(sameDayDate)) != nil) - XCTAssertEqual(DateFormatter().formatDate(sameYearDate), "Jan 24") - XCTAssertEqual(DateFormatter().formatDate(otherYearDate), "Jan 24, 2020") + if Calendar.current.isDateInToday(sameYearDate) { + XCTAssertEqual(dateFormatter.formatDate(sameYearDate), "18:34") + } else { + XCTAssertEqual(dateFormatter.formatDate(sameYearDate), "Jan 24") + } + XCTAssertEqual(dateFormatter.formatDate(otherYearDate), "Jan 24, 2020") } }