diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index f0a5dc2a4..ef590ff8b 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -30,13 +30,9 @@ blocks: - cache restore && make dependencies && cache store - mv ~/appium-env ~/git/flowcrypt-ios/appium/.env - sem-version node 16 && cache restore appium-npm && cd ./appium && npm i && cd .. && cache store appium-npm appium/node_modules - - brew install ideviceinstaller - - npm install ios-deploy -g --unsafe-perm=true --allow-root - - npm install ios-sim -g --unsafe-perm=true --allow-root jobs: - name: Appium UI tests commands: - - ios-sim start --devicetypeid com.apple.CoreSimulator.SimDeviceType.iPhone-13 - bundle exec fastlane build && ls -la appium - 'wget https://flowcrypt.s3.eu-central-1.amazonaws.com/dev/flowcrypt-ios-2021-12-17-v060-for-ci-migration-test.zip -P ~/git/flowcrypt-ios/appium' - cd appium && npm run-script lint diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index a6cbbadb3..7bbf6f48e 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -1053,6 +1053,15 @@ path = SignIn; sourceTree = ""; }; + 5161D87D27A488BD00130518 /* Contacts Service */ = { + isa = PBXGroup; + children = ( + D2FC1C0524D82C9F003B949D /* ContactsService.swift */, + D27B912024EFE842002DF0A1 /* Models */, + ); + path = "Contacts Service"; + sourceTree = ""; + }; 517C2E312779F91300FECF32 /* Models */ = { isa = PBXGroup; children = ( @@ -1177,8 +1186,9 @@ C132B9CA1EC2DE6400763715 /* GeneralConstants.swift */, 9F31AB9F232C071700CF87EA /* GlobalRouter.swift */, 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */, + 5161D87D27A488BD00130518 /* Contacts Service */, 9FB22CFD25715DDF0026EE64 /* Local Private Key Services */, - D27B911724EFE787002DF0A1 /* Local Pub Key Services */, + D27B911724EFE787002DF0A1 /* Local Contacts Service */, A381F24F271C1CA300A3D13C /* Remote Private Key Services */, A381F24D271C1C1100A3D13C /* Remote Pub Key Services */, ); @@ -1928,14 +1938,12 @@ path = Extensions; sourceTree = ""; }; - D27B911724EFE787002DF0A1 /* Local Pub Key Services */ = { + D27B911724EFE787002DF0A1 /* Local Contacts Service */ = { isa = PBXGroup; children = ( - D2FC1C0524D82C9F003B949D /* ContactsService.swift */, D27B911824EFE79F002DF0A1 /* LocalContactsProvider.swift */, - D27B912024EFE842002DF0A1 /* Models */, ); - path = "Local Pub Key Services"; + path = "Local Contacts Service"; sourceTree = ""; }; D27B912024EFE842002DF0A1 /* Models */ = { diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 82554dc49..946636902 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -947,7 +947,7 @@ extension ComposeViewController { extension ComposeViewController { private func searchEmail(with query: String) { Task { - let localEmails = contactsService.searchContacts(query: query) + let localEmails = contactsService.searchLocalContacts(query: query) let cloudEmails = try? await service.searchContacts(query: query) let emails = Set([localEmails, cloudEmails].compactMap { $0 }.flatMap { $0 }) updateState(with: .searchEmails(Array(emails))) @@ -956,21 +956,36 @@ extension ComposeViewController { private func evaluate(recipient: ComposeMessageRecipient) { guard recipient.email.isValidEmail else { - handleEvaluation(for: recipient, with: self.decorator.recipientInvalidEmailState, keyState: nil) + handleEvaluation(for: recipient, with: decorator.recipientInvalidEmailState) return } Task { do { - let contact = try await service.searchContact(with: recipient.email) - let state = getRecipientState(from: contact) - handleEvaluation(for: recipient, with: state, keyState: contact.keyState) + if let contact = try await service.findLocalContact(with: recipient.email) { + handleEvaluation(for: contact) + } + + let contactWithFetchedKeys = try await service.fetchContact(with: recipient.email) + handleEvaluation(for: contactWithFetchedKeys) } catch { handleEvaluation(error: error, with: recipient) } } } + private func handleEvaluation(for recipient: RecipientWithSortedPubKeys) { + let state = getRecipientState(from: recipient) + + let composeRecipient = ComposeMessageRecipient( + email: recipient.email, + state: state, + keyState: recipient.keyState + ) + + handleEvaluation(for: composeRecipient) + } + private func getRecipientState(from recipient: RecipientWithSortedPubKeys) -> RecipientState { switch recipient.keyState { case .active: @@ -984,15 +999,11 @@ extension ComposeViewController { } } - private func handleEvaluation( - for recipient: ComposeMessageRecipient, - with state: RecipientState, - keyState: PubKeyState? - ) { + private func handleEvaluation(for composeRecipient: ComposeMessageRecipient, with state: RecipientState? = nil) { updateRecipientWithNew( - state: state, - keyState: keyState, - for: .left(recipient) + state: state ?? composeRecipient.state, + keyState: composeRecipient.keyState, + for: .left(composeRecipient) ) } @@ -1021,7 +1032,7 @@ extension ComposeViewController { let index: Int? = { switch context { case let .left(recipient): - return recipients.firstIndex(where: { $0.email == recipient.email }) + return recipients.firstIndex(of: recipient) case let .right(index): return index.row } @@ -1074,6 +1085,7 @@ extension ComposeViewController { } } + // MARK: - Message password private func setMessagePassword() { Task { contextToSend.messagePassword = await enterMessagePassword() @@ -1411,7 +1423,11 @@ private actor ServiceActor { return try await cloudContactProvider.searchContacts(query: query) } - func searchContact(with email: String) async throws -> RecipientWithSortedPubKeys { - return try await contactsService.searchContact(with: email) + func findLocalContact(with email: String) async throws -> RecipientWithSortedPubKeys? { + return try await contactsService.findLocalContact(with: email) + } + + func fetchContact(with email: String) async throws -> RecipientWithSortedPubKeys { + return try await contactsService.fetchContact(with: email) } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift index 4c518f27d..5131730f6 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift @@ -223,13 +223,13 @@ final class MessageService { // MARK: - Message verification extension MessageService { - private func fetchVerificationPubKeys(for sender: String?, onlyLocal: Bool) async throws -> [String] { - guard let sender = sender else { return [] } + private func fetchVerificationPubKeys(for email: String?, onlyLocal: Bool) async throws -> [String] { + guard let email = email else { return [] } - let pubKeys = contactsService.retrievePubKeys(for: sender) + let pubKeys = contactsService.retrievePubKeys(for: email) if pubKeys.isNotEmpty || onlyLocal { return pubKeys } - guard let contact = try? await contactsService.searchContact(with: sender) + guard let contact = try? await contactsService.fetchContact(with: email) else { return [] } return contact.pubKeys.map(\.armored) diff --git a/FlowCrypt/Functionality/Services/Local Pub Key Services/ContactsService.swift b/FlowCrypt/Functionality/Services/Contacts Service/ContactsService.swift similarity index 72% rename from FlowCrypt/Functionality/Services/Local Pub Key Services/ContactsService.swift rename to FlowCrypt/Functionality/Services/Contacts Service/ContactsService.swift index 462ca1140..85f4d26f1 100644 --- a/FlowCrypt/Functionality/Services/Local Pub Key Services/ContactsService.swift +++ b/FlowCrypt/Functionality/Services/Contacts Service/ContactsService.swift @@ -17,8 +17,9 @@ protocol ContactsServiceType: PublicKeyProvider, ContactsProviderType { } protocol ContactsProviderType { - func searchContact(with email: String) async throws -> RecipientWithSortedPubKeys - func searchContacts(query: String) -> [String] + func findLocalContact(with email: String) async throws -> RecipientWithSortedPubKeys? + func searchLocalContacts(query: String) -> [String] + func fetchContact(with email: String) async throws -> RecipientWithSortedPubKeys } protocol PublicKeyProvider { @@ -42,22 +43,19 @@ struct ContactsService: ContactsServiceType { } extension ContactsService: ContactsProviderType { - func searchContact(with email: String) async throws -> RecipientWithSortedPubKeys { - let contact = try await localContactsProvider.searchRecipient(with: email) - guard let contact = contact else { - let recipient = try await pubLookup.lookup(email: email) - try localContactsProvider.save(recipient: recipient) - return recipient - } - - let recipient = try await pubLookup.lookup(email: email) - try localContactsProvider.updateKeys(for: recipient) - return contact + func findLocalContact(with email: String) async throws -> RecipientWithSortedPubKeys? { + return try await localContactsProvider.searchRecipient(with: email) } - func searchContacts(query: String) -> [String] { + func searchLocalContacts(query: String) -> [String] { localContactsProvider.searchEmails(query: query) } + + func fetchContact(with email: String) async throws -> RecipientWithSortedPubKeys { + let recipient = try await pubLookup.lookup(email: email) + try localContactsProvider.updateKeys(for: recipient) + return recipient + } } extension ContactsService: PublicKeyProvider { diff --git a/FlowCrypt/Functionality/Services/Local Pub Key Services/Models/PubKey.swift b/FlowCrypt/Functionality/Services/Contacts Service/Models/PubKey.swift similarity index 100% rename from FlowCrypt/Functionality/Services/Local Pub Key Services/Models/PubKey.swift rename to FlowCrypt/Functionality/Services/Contacts Service/Models/PubKey.swift diff --git a/FlowCrypt/Functionality/Services/Local Pub Key Services/Models/PubKeyState.swift b/FlowCrypt/Functionality/Services/Contacts Service/Models/PubKeyState.swift similarity index 100% rename from FlowCrypt/Functionality/Services/Local Pub Key Services/Models/PubKeyState.swift rename to FlowCrypt/Functionality/Services/Contacts Service/Models/PubKeyState.swift diff --git a/FlowCrypt/Functionality/Services/Local Pub Key Services/Models/RecipientWithSortedPubKeys.swift b/FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift similarity index 100% rename from FlowCrypt/Functionality/Services/Local Pub Key Services/Models/RecipientWithSortedPubKeys.swift rename to FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift diff --git a/FlowCrypt/Functionality/Services/Local Pub Key Services/LocalContactsProvider.swift b/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift similarity index 99% rename from FlowCrypt/Functionality/Services/Local Pub Key Services/LocalContactsProvider.swift rename to FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift index 690d57ac0..c32aa0e32 100644 --- a/FlowCrypt/Functionality/Services/Local Pub Key Services/LocalContactsProvider.swift +++ b/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift @@ -129,7 +129,7 @@ extension LocalContactsProvider { private func parseRecipient(from recipient: Recipient) async throws -> RecipientWithSortedPubKeys { let armoredToParse = recipient.pubKeys - .map { $0.armored } + .map(\.armored) .joined(separator: "\n") let parsed = try await core.parseKeys(armoredOrBinary: armoredToParse.data()) return RecipientWithSortedPubKeys(recipient, keyDetails: parsed.keyDetails) diff --git a/FlowCryptAppTests/Mocks/ContactsServiceMock.swift b/FlowCryptAppTests/Mocks/ContactsServiceMock.swift index 7a899f349..75dddc909 100644 --- a/FlowCryptAppTests/Mocks/ContactsServiceMock.swift +++ b/FlowCryptAppTests/Mocks/ContactsServiceMock.swift @@ -15,9 +15,9 @@ final class ContactsServiceMock: ContactsServiceType { retrievePubKeysResult(email) } - var searchContactResult: Result! - func searchContact(with email: String) async throws -> RecipientWithSortedPubKeys { - switch searchContactResult { + var fetchContactResult: Result! + func fetchContact(with email: String) async throws -> RecipientWithSortedPubKeys { + switch fetchContactResult { case .success(let result): return result case .failure(let error): @@ -26,7 +26,19 @@ final class ContactsServiceMock: ContactsServiceType { fatalError() } } - func searchContacts(query: String) -> [String] { [] } + + var findLocalContactResult: Result! + func findLocalContact(with email: String) async throws -> RecipientWithSortedPubKeys? { + switch findLocalContactResult { + case .success(let result): + return result + case .failure(let error): + throw error + default: + fatalError() + } + } + func searchLocalContacts(query: String) -> [String] { [] } func removePubKey(with fingerprint: String, for email: String) {} } diff --git a/Gemfile.lock b/Gemfile.lock index e51f6de72..1ed9f6c46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,7 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.549.0) + aws-partitions (1.551.0) aws-sdk-core (3.125.5) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.525.0) @@ -116,7 +116,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.201.2) + fastlane (2.203.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -160,7 +160,7 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.15.0) + google-apis-androidpublisher_v3 (0.16.0) google-apis-core (>= 0.4, < 2.a) google-apis-core (0.4.2) addressable (~> 2.5, >= 2.5.1) @@ -203,7 +203,7 @@ GEM http-cookie (1.0.4) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.8.11) + i18n (1.9.1) concurrent-ruby (~> 1.0) jmespath (1.5.0) json (2.6.1) @@ -284,7 +284,7 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - zeitwerk (2.5.3) + zeitwerk (2.5.4) PLATFORMS arm64-darwin-21