diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index cf13ff177..d8be2f579 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 51074E6427BD0C5800FBB124 /* RecipientToggleButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51074E6327BD0C5800FBB124 /* RecipientToggleButtonNode.swift */; }; 5109A77C272153B400D2CEB9 /* LeftAlignedCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5109A77B272153B400D2CEB9 /* LeftAlignedCollectionViewFlowLayout.swift */; }; + 510BB63527BE92CC00B1011F /* RecipientBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510BB63427BE92CC00B1011F /* RecipientBase.swift */; }; 511D07E12769FBBA0050417B /* MessagePasswordCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511D07E02769FBBA0050417B /* MessagePasswordCellNode.swift */; }; 511D07E3276A2DF80050417B /* ButtonWithPaddingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511D07E2276A2DF80050417B /* ButtonWithPaddingNode.swift */; }; 512C1414271077F8002DE13F /* GoogleAPIClientForREST_PeopleService in Frameworks */ = {isa = PBXBuildFile; productRef = 512C1413271077F8002DE13F /* GoogleAPIClientForREST_PeopleService */; }; @@ -506,6 +507,7 @@ 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentNode.swift; sourceTree = ""; }; 51074E6327BD0C5800FBB124 /* RecipientToggleButtonNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientToggleButtonNode.swift; sourceTree = ""; }; 5109A77B272153B400D2CEB9 /* LeftAlignedCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftAlignedCollectionViewFlowLayout.swift; sourceTree = ""; }; + 510BB63427BE92CC00B1011F /* RecipientBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientBase.swift; sourceTree = ""; }; 511D07E02769FBBA0050417B /* MessagePasswordCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePasswordCellNode.swift; sourceTree = ""; }; 511D07E2276A2DF80050417B /* ButtonWithPaddingNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonWithPaddingNode.swift; sourceTree = ""; }; 5133B66F2716320F00C95463 /* ContactKeyDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactKeyDetailViewController.swift; sourceTree = ""; }; @@ -990,6 +992,7 @@ 2C141B2E274578C20038A3F8 /* Keypair.swift */, 5180CB9227357B67001FC7EF /* RawClientConfiguration.swift */, 2C141B2B274572D50038A3F8 /* Recipient.swift */, + 510BB63427BE92CC00B1011F /* RecipientBase.swift */, 2CC50FAE27440B2C0051629A /* Session.swift */, 9F0C3C132316E69300299985 /* User.swift */, ); @@ -2712,6 +2715,7 @@ 9F31AB8E23298BCF00CF87EA /* Imap+folders.swift in Sources */, D2891AC224C59EFA008918E3 /* KeyService.swift in Sources */, D269E02724103A20000495C3 /* ComposeViewControllerInput.swift in Sources */, + 510BB63527BE92CC00B1011F /* RecipientBase.swift in Sources */, 9FAFD75D2714A06400321FA4 /* InboxProviders.swift in Sources */, 2C141B2C274572D50038A3F8 /* Recipient.swift in Sources */, 9F0C3C142316E69300299985 /* User.swift in Sources */, diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index e2a82ccd7..aec3ae2f5 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -18,7 +18,7 @@ import PhotosUI final class ComposeViewController: TableNodeViewController { private enum Constants { - static let endTypingCharacters = [",", " ", "\n", ";"] + static let endTypingCharacters = [",", "\n", ";"] static let minRecipientsPartHeight: CGFloat = 32 } @@ -29,7 +29,7 @@ final class ComposeViewController: TableNodeViewController { } private enum State { - case main, searchEmails([String]) + case main, searchEmails([Recipient]) } enum Section: Hashable { @@ -49,7 +49,6 @@ final class ComposeViewController: TableNodeViewController { } private var userFinishedSearching = false - private var isRecipientLoading = false private var userTappedOutSideRecipientsArea = false private var shouldShowEmailRecipientsLabel = false private let appContext: AppContextWithUser @@ -199,13 +198,26 @@ final class ComposeViewController: TableNodeViewController { func update(with message: Message) { self.contextToSend.subject = message.subject self.contextToSend.message = message.raw - self.contextToSend.recipients = [ - ComposeMessageRecipient( - email: "tom@flowcrypt.com", - type: .to, - state: decorator.recipientIdleState - ) - ] + message.to.forEach { recipient in + evaluateMessage(recipient: recipient, type: .to) + } + message.cc.forEach { recipient in + evaluateMessage(recipient: recipient, type: .cc) + } + message.bcc.forEach { recipient in + evaluateMessage(recipient: recipient, type: .bcc) + } + } + + func evaluateMessage(recipient: Recipient, type: RecipientType) { + let recipient = ComposeMessageRecipient( + email: recipient.email, + name: recipient.name, + type: type, + state: decorator.recipientIdleState + ) + contextToSend.add(recipient: recipient) + evaluate(recipient: recipient) } private func observeComposeUpdates() { @@ -326,16 +338,12 @@ extension ComposeViewController { private func setupQuote() { guard input.isQuote else { return } - input.quoteRecipients.forEach { email in - let recipient = ComposeMessageRecipient(email: email, type: .to, state: decorator.recipientIdleState) - contextToSend.add(recipient: recipient) - evaluate(recipient: recipient) + input.quoteRecipients.forEach { recipient in + evaluateMessage(recipient: recipient, type: .to) } - input.quoteCCRecipients.forEach { email in - let recipient = ComposeMessageRecipient(email: email, type: .cc, state: decorator.recipientIdleState) - contextToSend.add(recipient: recipient) - evaluate(recipient: recipient) + input.quoteCCRecipients.forEach { recipient in + evaluateMessage(recipient: recipient, type: .cc) } if input.quoteCCRecipients.isNotEmpty { @@ -633,10 +641,21 @@ extension ComposeViewController: ASTableDelegate, ASTableDataSource { return ASCellNode() } return self.attachmentNode(for: indexPath.row) - case let (.searchEmails(emails), .searchResults): + case let (.searchEmails(recipients), .searchResults): guard indexPath.row > 0 else { return DividerCellNode() } - guard emails.isNotEmpty else { return self.noSearchResultsNode() } - return InfoCellNode(input: self.decorator.styledRecipientInfo(with: emails[indexPath.row-1])) + guard recipients.isNotEmpty else { return self.noSearchResultsNode() } + guard let recipient = recipients[safe: indexPath.row-1] else { return ASCellNode() } + + if let name = recipient.name { + let input = self.decorator.styledRecipientInfo( + with: recipient.email, + name: name + ) + return LabelCellNode(input: input) + } else { + let input = self.decorator.styledRecipientInfo(with: recipient.email) + return InfoCellNode(input: input) + } case (.searchEmails, .contacts): return indexPath.row == 0 ? DividerCellNode() : self.enableGoogleContactsNode() default: @@ -646,13 +665,13 @@ extension ComposeViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_ tableNode: ASTableNode, didSelectRowAt indexPath: IndexPath) { - if case let .searchEmails(emails) = state, let recipientType = selectedRecipientType { + if case let .searchEmails(recipients) = state, let recipientType = selectedRecipientType { guard let section = sectionsList[safe: indexPath.section] else { return } switch section { case .searchResults: - let selectedEmail = emails[safe: indexPath.row-1] - handleEndEditingAction(with: selectedEmail, for: recipientType) + let recipient = recipients[safe: indexPath.row-1] + handleEndEditingAction(with: recipient?.email, name: recipient?.name, for: recipientType) case .contacts: askForContactsPermission() default: @@ -692,7 +711,8 @@ extension ComposeViewController { } private func showRecipientLabelIfNecessary() { - guard !self.isRecipientLoading, + let isRecipientLoading = self.contextToSend.recipients.filter { $0.state == decorator.recipientIdleState }.isNotEmpty + guard !isRecipientLoading, self.contextToSend.recipients.isNotEmpty, self.userTappedOutSideRecipientsArea else { return @@ -963,9 +983,9 @@ extension ComposeViewController { } } - private func handleEndEditingAction(with text: String?, for recipientType: RecipientType) { + private func handleEndEditingAction(with email: String?, name: String? = nil, for recipientType: RecipientType) { guard shouldEvaluateRecipientInput, - let text = text, text.isNotEmpty + let email = email, email.isNotEmpty else { return } let recipients = contextToSend.recipients(type: recipientType) @@ -984,7 +1004,13 @@ extension ComposeViewController { contextToSend.set(recipients: idleRecipients, for: recipientType) - let newRecipient = ComposeMessageRecipient(email: text, type: recipientType, state: decorator.recipientIdleState) + let newRecipient = ComposeMessageRecipient( + email: email, + name: name, + type: recipientType, + state: decorator.recipientIdleState + ) + let indexOfRecipient: Int let indexPath = recipientsIndexPath(type: recipientType, part: .list) @@ -1090,10 +1116,14 @@ extension ComposeViewController { private func searchEmail(with query: String) { Task { do { - let localEmails = try localContactsProvider.searchEmails(query: query) - let cloudEmails = try? await cloudContactProvider.searchContacts(query: query) - let emails = Set([localEmails, cloudEmails].compactMap { $0 }.flatMap { $0 }) - updateState(with: .searchEmails(Array(emails))) + let cloudRecipients = try await cloudContactProvider.searchContacts(query: query) + let localRecipients = try localContactsProvider.searchRecipients(query: query) + + let recipients = (cloudRecipients + localRecipients) + .unique() + .sorted() + + updateState(with: .searchEmails(recipients)) } catch { showAlert(message: error.localizedDescription) } @@ -1110,7 +1140,6 @@ extension ComposeViewController { } Task { - isRecipientLoading = true var localContact: RecipientWithSortedPubKeys? do { if let contact = try await localContactsProvider.searchRecipient(with: recipient.email) { @@ -1118,13 +1147,13 @@ extension ComposeViewController { handleEvaluation(for: contact) } - let contactWithFetchedKeys = try await pubLookup.fetchRemoteUpdateLocal(with: recipient.email) + let contact = Recipient(recipient: recipient) + let contactWithFetchedKeys = try await pubLookup.fetchRemoteUpdateLocal(with: contact) + handleEvaluation(for: contactWithFetchedKeys) - isRecipientLoading = false showRecipientLabelIfNecessary() } catch { handleEvaluation(error: error, with: recipient.email, contact: localContact) - isRecipientLoading = false showRecipientLabelIfNecessary() } } diff --git a/FlowCrypt/Controllers/Compose/ComposeViewControllerInput.swift b/FlowCrypt/Controllers/Compose/ComposeViewControllerInput.swift index 5fbbaf73e..887bbee95 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewControllerInput.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewControllerInput.swift @@ -12,9 +12,9 @@ struct ComposeMessageInput: Equatable { static let empty = ComposeMessageInput(type: .idle) struct MessageQuoteInfo: Equatable { - let recipients: [String] - let ccRecipients: [String] - let sender: String? + let recipients: [Recipient] + let ccRecipients: [Recipient] + let sender: Recipient? let subject: String? let mime: Data? let sentDate: Date @@ -31,14 +31,14 @@ struct ComposeMessageInput: Equatable { let type: InputType - var quoteRecipients: [String] { + var quoteRecipients: [Recipient] { guard case .reply(let info) = type else { return [] } return info.recipients } - var quoteCCRecipients: [String] { + var quoteCCRecipients: [Recipient] { guard case .reply(let info) = type else { return [] } diff --git a/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift b/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift index fbbb76b7e..cd5414466 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift @@ -78,6 +78,23 @@ struct ComposeViewDecorator { ) } + func styledRecipientInfo(with email: String, name: String) -> LabelCellNode.Input { + LabelCellNode.Input( + title: name.attributed( + .medium(17), + color: .mainTextColor.withAlphaComponent(0.8), + alignment: .left + ), + text: email.attributed( + .regular(15), + color: .mainTextColor.withAlphaComponent(0.5), + alignment: .left + ), + insets: .deviceSpecificTextInsets(top: 8, bottom: 8), + spacing: 0 + ) + } + func styledMessage(with text: String) -> NSAttributedString { text.attributed(.regular(17)) } @@ -95,7 +112,7 @@ struct ComposeViewDecorator { dateFormatter.timeStyle = .short let time = dateFormatter.string(from: info.sentDate) - let from = info.sender ?? "unknown sender" + let from = info.sender?.email ?? "unknown sender" let text: String = "\n\n" + "compose_quote_from".localizeWithArguments(date, time, from) @@ -162,9 +179,9 @@ struct ComposeViewDecorator { color: UIColor, imageName: String ) -> MessagePasswordCellNode.Input { - .init( - text: text.attributed(.regular(14), color: color), - color: color, + .init( + text: text.attributed(.regular(14), color: color), + color: color, image: UIImage(systemName: imageName)?.tinted(color) ) } @@ -311,7 +328,7 @@ extension ComposeViewDecorator { extension RecipientEmailsCellNode.Input { init(_ recipient: ComposeMessageRecipient) { self.init( - email: recipient.email.lowercased().attributed( + email: recipient.displayName.attributed( .regular(17), color: recipient.state.textColor, alignment: .left diff --git a/FlowCrypt/Controllers/Inbox/InboxRenderable.swift b/FlowCrypt/Controllers/Inbox/InboxRenderable.swift index 7fe2e499f..f85b7d7b2 100644 --- a/FlowCrypt/Controllers/Inbox/InboxRenderable.swift +++ b/FlowCrypt/Controllers/Inbox/InboxRenderable.swift @@ -37,7 +37,7 @@ extension InboxRenderable { extension InboxRenderable { init(message: Message) { - self.title = message.sender ?? "message_unknown_sender".localized + self.title = message.sender?.shortName ?? "message_unknown_sender".localized self.messageCount = 1 self.subtitle = message.subject ?? "message_missing_subject".localized self.dateString = DateFormatter().formatDate(message.date) @@ -71,15 +71,14 @@ extension InboxRenderable { if folderPath == MessageLabelType.sent.value { let recipients = thread.messages .flatMap(\.allRecipients) - .map(\.displayName) + .map(\.shortName) .unique() .joined(separator: ", ") return "To: \(recipients)" } else { return thread.messages - .compactMap(\.sender) - .compactMap { $0.components(separatedBy: "@").first } + .compactMap(\.sender?.shortName) .unique() .joined(separator: ",") } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift index 333c71d0b..e495c0d66 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift @@ -14,7 +14,7 @@ struct ContactDetailDecorator { func userNodeInput(with contact: RecipientWithSortedPubKeys) -> ContactUserCellNode.Input { ContactUserCellNode.Input( - user: (contact.name ?? contact.email).attributed(.regular(16)) + user: contact.formatted.attributed(.regular(16)) ) } diff --git a/FlowCrypt/Controllers/Threads/ThreadDetailsDecorator.swift b/FlowCrypt/Controllers/Threads/ThreadDetailsDecorator.swift index ece131c45..4f90988e5 100644 --- a/FlowCrypt/Controllers/Threads/ThreadDetailsDecorator.swift +++ b/FlowCrypt/Controllers/Threads/ThreadDetailsDecorator.swift @@ -11,11 +11,11 @@ import UIKit extension ThreadMessageInfoCellNode.Input { init(threadMessage: ThreadDetailsViewController.Input, index: Int) { - let sender = threadMessage.rawMessage.sender ?? "message_unknown_sender".localized + let sender = threadMessage.rawMessage.sender?.displayName ?? "message_unknown_sender".localized let recipientPrefix = "to".localized let recipientsList = threadMessage.rawMessage .allRecipients - .map(\.displayName) + .map(\.shortName) .joined(separator: ", ") let recipientLabel = [recipientPrefix, recipientsList].joined(separator: " ") let date = DateFormatter().formatDate(threadMessage.rawMessage.date) @@ -34,7 +34,7 @@ extension ThreadMessageInfoCellNode.Input { signatureBadge: makeSignatureBadge(threadMessage), sender: .text(from: sender, style: style, color: .label), recipientLabel: .text(from: recipientLabel, style: style, color: .secondaryLabel), - recipients: threadMessage.rawMessage.recipients.map(\.rawString), + recipients: threadMessage.rawMessage.to.map(\.rawString), ccRecipients: threadMessage.rawMessage.cc.map(\.rawString), bccRecipients: threadMessage.rawMessage.bcc.map(\.rawString), date: .text(from: date, style: style, color: dateColor), diff --git a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController.swift b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController.swift index f52d18e73..8e57d68f0 100644 --- a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController.swift +++ b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController.swift @@ -224,15 +224,15 @@ extension ThreadDetailsViewController { else { return } let sender = [input.rawMessage.sender].compactMap { $0 } - let ccRecipients = quoteType == .replyAll ? input.rawMessage.cc.map(\.email) : [] - let recipients: [String] = { + + let ccRecipients = quoteType == .replyAll ? input.rawMessage.cc : [] + let recipients: [Recipient] = { switch quoteType { case .reply: return sender case .replyAll: - let recipientEmails = input.rawMessage.recipients.map(\.email) - let allRecipients = (recipientEmails + sender).unique() - let filteredRecipients = allRecipients.filter { $0 != appContext.user.email } + let allRecipients = (input.rawMessage.to + sender).unique() + let filteredRecipients = allRecipients.filter { $0.email != appContext.user.email } return filteredRecipients.isEmpty ? sender : filteredRecipients case .forward: return [] diff --git a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift index f40321669..4c0d0159f 100644 --- a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift @@ -11,7 +11,7 @@ import GoogleAPIClientForREST_PeopleService protocol CloudContactsProvider { var isContactsScopeEnabled: Bool { get } - func searchContacts(query: String) async throws -> [String] + func searchContacts(query: String) async throws -> [Recipient] } enum CloudContactsProviderError: Error { @@ -55,7 +55,7 @@ final class UserContactsProvider { var readMask: String { switch self { - case .contacts, .other: return "emailAddresses" + case .contacts, .other: return "names,emailAddresses" } } } @@ -81,24 +81,27 @@ final class UserContactsProvider { } extension UserContactsProvider: CloudContactsProvider { - func searchContacts(query: String) async -> [String] { + func searchContacts(query: String) async -> [Recipient] { guard isContactsScopeEnabled else { return [] } let contacts = await searchUserContacts(query: query, type: .contacts) let otherContacts = await searchUserContacts(query: query, type: .other) - let emails = Set(contacts + otherContacts) - return Array(emails).sorted(by: >) + let allRecipients = (contacts + otherContacts) + .map(Recipient.init) + .unique() + .sorted() + return allRecipients } } extension UserContactsProvider { - private func searchUserContacts(query: String, type: QueryType) async -> [String] { + private func searchUserContacts(query: String, type: QueryType) async -> [Recipient] { let query = type.query(searchString: query) guard let emails = try? await perform(query: query) else { return [] } return emails } - private func perform(query: GTLRPeopleServiceQuery) async throws -> [String] { + private func perform(query: GTLRPeopleServiceQuery) async throws -> [Recipient] { try await withCheckedThrowingContinuation { continuation in self.peopleService.executeQuery(query) { _, data, error in if let error = error { @@ -113,12 +116,8 @@ extension UserContactsProvider { return continuation.resume(throwing: CloudContactsProviderError.failedToParseData(data)) } - let emails = contacts - .compactMap { $0.person?.emailAddresses } - .flatMap { $0 } - .compactMap { $0.value } - - return continuation.resume(returning: emails) + let recipients = contacts.compactMap(\.person).compactMap(Recipient.init) + return continuation.resume(returning: recipients) } } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift index 3c7551bef..ff7e1607e 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift @@ -106,7 +106,7 @@ final class MessageService { func decryptAndProcessMessage( mime rawMimeData: Data, - sender: String?, + sender: Recipient?, onlyLocalKeys: Bool ) async throws -> ProcessedMessage { let keys = try await keyService.getPrvKeyInfo() @@ -152,7 +152,7 @@ final class MessageService { private func processMessage( rawMimeData: Data, - sender: String?, + sender: Recipient?, with decrypted: CoreRes.ParseDecryptMsg, keys: [PrvKeyInfo] ) async throws -> ProcessedMessage { @@ -232,13 +232,13 @@ final class MessageService { // MARK: - Message verification extension MessageService { - private func fetchVerificationPubKeys(for email: String?, onlyLocal: Bool) async throws -> [String] { - guard let email = email else { return [] } + private func fetchVerificationPubKeys(for sender: Recipient?, onlyLocal: Bool) async throws -> [String] { + guard let sender = sender else { return [] } - let pubKeys = try localContactsProvider.retrievePubKeys(for: email) + let pubKeys = try localContactsProvider.retrievePubKeys(for: sender.email) if pubKeys.isNotEmpty || onlyLocal { return pubKeys } - guard let contact = try? await pubLookup.fetchRemoteUpdateLocal(with: email) + guard let contact = try? await pubLookup.fetchRemoteUpdateLocal(with: sender) else { return [] } return contact.pubKeys.map(\.armored) diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift index bb9a574e0..2f219241b 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift @@ -49,10 +49,15 @@ extension Message { init(imapMessage: MCOIMAPMessage) { // swiftlint:disable compiler_protocol_init let labels = Array(arrayLiteral: imapMessage.flags).map(MessageLabelType.init).map(MessageLabel.init) + var sender: Recipient? + if let senderString = imapMessage.header.from ?? imapMessage.header.sender { + sender = Recipient(senderString.nonEncodedRFC822String()) + } + self.init( identifier: Identifier(intId: Int(imapMessage.uid)), date: imapMessage.header.date, - sender: imapMessage.header.from.mailbox ?? imapMessage.header.sender.mailbox, + sender: sender, subject: imapMessage.header.subject, size: Int(imapMessage.size), labels: labels, diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift index ceef163e2..01adfe054 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift @@ -13,10 +13,10 @@ import GoogleAPIClientForREST_Gmail struct Message: Hashable { let identifier: Identifier let date: Date - let sender: String? - let recipients: [MessageRecipient] - let cc: [MessageRecipient] - let bcc: [MessageRecipient] + let sender: Recipient? + let to: [Recipient] + let cc: [Recipient] + let bcc: [Recipient] let subject: String? let size: Int? let attachmentIds: [String] @@ -41,7 +41,7 @@ struct Message: Hashable { init( identifier: Identifier, date: Date, - sender: String?, + sender: Recipient?, subject: String?, size: Int?, labels: [MessageLabel], @@ -49,7 +49,7 @@ struct Message: Hashable { threadId: String? = nil, draftIdentifier: String? = nil, raw: String? = nil, - recipient: String? = nil, + to: String? = nil, cc: String? = nil, bcc: String? = nil ) { @@ -63,7 +63,7 @@ struct Message: Hashable { self.threadId = threadId self.draftIdentifier = draftIdentifier self.raw = raw - self.recipients = Message.parseRecipients(recipient) + self.to = Message.parseRecipients(to) self.cc = Message.parseRecipients(cc) self.bcc = Message.parseRecipients(bcc) } @@ -80,8 +80,8 @@ extension Message: Equatable, Comparable { } extension Message { - static func parseRecipients(_ string: String?) -> [MessageRecipient] { - string?.components(separatedBy: ", ").map(MessageRecipient.init) ?? [] + static func parseRecipients(_ string: String?) -> [Recipient] { + string?.components(separatedBy: ", ").map(Recipient.init) ?? [] } } @@ -97,8 +97,8 @@ extension Message { return copy } - var allRecipients: [MessageRecipient] { - [recipients, cc, bcc].flatMap { $0 } + var allRecipients: [Recipient] { + [to, cc, bcc].flatMap { $0 } } } @@ -111,35 +111,3 @@ struct Identifier: Equatable, Hashable { self.intId = intId } } - -struct MessageRecipient: Hashable { - let name: String? - let email: String - - init(_ string: String) { - let parts = string.components(separatedBy: " ") - - guard parts.count > 1, let email = parts.last else { - self.name = nil - self.email = string - return - } - - self.email = email.filter { !["<", ">"].contains($0) } - let name = string - .replacingOccurrences(of: email, with: "") - .replacingOccurrences(of: "\"", with: "") - .trimmingCharacters(in: .whitespaces) - self.name = name == self.email ? nil : name - } -} - -extension MessageRecipient { - var displayName: String { - name?.components(separatedBy: " ").first ?? - email.components(separatedBy: "@").first ?? - "unknown" - } - - var rawString: (String?, String) { (name, email) } -} diff --git a/FlowCrypt/Functionality/Mail Provider/Threads/MessagesThreadProvider.swift b/FlowCrypt/Functionality/Mail Provider/Threads/MessagesThreadProvider.swift index 0cbc95cdb..2724d862c 100644 --- a/FlowCrypt/Functionality/Mail Provider/Threads/MessagesThreadProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/Threads/MessagesThreadProvider.swift @@ -145,30 +145,27 @@ extension Message { let labelTypes: [MessageLabelType] = message.labelIds?.map(MessageLabelType.init) ?? [] let labels = labelTypes.map(MessageLabel.init) - var sender: String? + var sender: Recipient? var subject: String? - var recipient: String? + var to: String? var cc: String? var bcc: String? messageHeaders.compactMap { $0 }.forEach { - guard let name = $0.name?.lowercased() else { return } - let value = $0.value + guard let name = $0.name?.lowercased(), + let value = $0.value + else { return } + switch name { - case .from: sender = value + case .from: sender = Recipient(value) case .subject: subject = value - case .to: recipient = value + case .to: to = value case .cc: cc = value case .bcc: bcc = value default: break } } - // TODO: - Tom 3 - // Gmail returns sender string as "Google security " - // slice it to previous format, like "googleaccount-noreply@gmail.com" - sender = sender?.slice(from: "<", to: ">") ?? sender - self.init( identifier: Identifier(stringId: identifier), // Should be divided by 1000, because Date(timeIntervalSince1970:) expects seconds @@ -182,7 +179,7 @@ extension Message { threadId: message.threadId, draftIdentifier: draftIdentifier, raw: message.raw, - recipient: recipient, + to: to, cc: cc, bcc: bcc ) diff --git a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageContext.swift b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageContext.swift index 9feb95d76..859d9ab79 100644 --- a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageContext.swift +++ b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageContext.swift @@ -60,7 +60,7 @@ extension ComposeMessageContext { } func recipientEmails(type: RecipientType) -> [String] { - recipients(type: type).map(\.email) + recipients(type: type).map(\.formatted) } func recipient(at index: Int, type: RecipientType) -> ComposeMessageRecipient? { diff --git a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageRecipient.swift b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageRecipient.swift index 4134f6bd0..d4b5f53ea 100644 --- a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageRecipient.swift +++ b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageRecipient.swift @@ -8,8 +8,9 @@ import Foundation -struct ComposeMessageRecipient { +struct ComposeMessageRecipient: RecipientBase { let email: String + let name: String? let type: RecipientType var state: RecipientState var keyState: PubKeyState? diff --git a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift index 5eb1c8ddf..934008a8c 100644 --- a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift +++ b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift @@ -144,12 +144,15 @@ final class ComposeMessageService { ) } - private func getRecipientKeys(for recipients: [ComposeMessageRecipient]) async throws -> [RecipientWithSortedPubKeys] { + private func getRecipientKeys(for composeRecipients: [ComposeMessageRecipient]) async throws -> [RecipientWithSortedPubKeys] { + let recipients = composeRecipients.map(Recipient.init) var recipientsWithKeys: [RecipientWithSortedPubKeys] = [] for recipient in recipients { let armoredPubkeys = try localContactsProvider.retrievePubKeys(for: recipient.email).joined(separator: "\n") let parsed = try await self.core.parseKeys(armoredOrBinary: armoredPubkeys.data()) - recipientsWithKeys.append(RecipientWithSortedPubKeys(email: recipient.email, keyDetails: parsed.keyDetails)) + recipientsWithKeys.append( + RecipientWithSortedPubKeys(recipient, keyDetails: parsed.keyDetails) + ) } return recipientsWithKeys } diff --git a/FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift b/FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift index e8b44c182..1f5af0228 100644 --- a/FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift +++ b/FlowCrypt/Functionality/Services/Contacts Service/Models/RecipientWithSortedPubKeys.swift @@ -8,7 +8,7 @@ import Foundation -struct RecipientWithSortedPubKeys { +struct RecipientWithSortedPubKeys: RecipientBase { let email: String /// name if known let name: String? @@ -26,21 +26,12 @@ struct RecipientWithSortedPubKeys { extension RecipientWithSortedPubKeys { init(_ recipient: Recipient, keyDetails: [KeyDetails] = []) { self.email = recipient.email - self.name = recipient.name + self.name = recipient.name ?? keyDetails.first?.users.first self.lastUsed = recipient.lastUsed self._pubKeys = keyDetails.map(PubKey.init) } } -extension RecipientWithSortedPubKeys { - init(email: String, keyDetails: [KeyDetails]) { - self.email = email - self.name = keyDetails.first?.users.first ?? email - self.lastUsed = nil - self._pubKeys = keyDetails.map(PubKey.init) - } -} - extension RecipientWithSortedPubKeys { mutating func remove(pubKey: PubKey) { pubKeys.removeAll(where: { $0 == pubKey }) diff --git a/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift b/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift index bab231489..a4c8c91ad 100644 --- a/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift +++ b/FlowCrypt/Functionality/Services/Local Contacts Service/LocalContactsProvider.swift @@ -22,7 +22,7 @@ protocol PublicKeyProvider { protocol LocalContactsProviderType: PublicKeyProvider { func searchRecipient(with email: String) async throws -> RecipientWithSortedPubKeys? - func searchEmails(query: String) throws -> [String] + func searchRecipients(query: String) throws -> [Recipient] func save(recipient: RecipientWithSortedPubKeys) throws func remove(recipient: RecipientWithSortedPubKeys) throws func updateKeys(for recipient: RecipientWithSortedPubKeys) throws @@ -101,10 +101,11 @@ extension LocalContactsProvider: LocalContactsProviderType { return try await parseRecipient(from: recipient) } - func searchEmails(query: String) throws -> [String] { - try storage.objects(RecipientRealmObject.self) + func searchRecipients(query: String) throws -> [Recipient] { + try storage + .objects(RecipientRealmObject.self) .filter("email contains[c] %@", query) - .map(\.email) + .map(Recipient.init) } func getAllRecipients() async throws -> [RecipientWithSortedPubKeys] { diff --git a/FlowCrypt/Functionality/Services/Remote Pub Key Services/PubLookup.swift b/FlowCrypt/Functionality/Services/Remote Pub Key Services/PubLookup.swift index 989d4256f..be3686568 100644 --- a/FlowCrypt/Functionality/Services/Remote Pub Key Services/PubLookup.swift +++ b/FlowCrypt/Functionality/Services/Remote Pub Key Services/PubLookup.swift @@ -7,8 +7,8 @@ // protocol PubLookupType { - func lookup(email: String) async throws -> RecipientWithSortedPubKeys - func fetchRemoteUpdateLocal(with email: String) async throws -> RecipientWithSortedPubKeys + func lookup(recipient: Recipient) async throws -> RecipientWithSortedPubKeys + func fetchRemoteUpdateLocal(with recipient: Recipient) async throws -> RecipientWithSortedPubKeys } class PubLookup: PubLookupType { @@ -37,14 +37,14 @@ class PubLookup: PubLookupType { self.attesterApi = attesterApi ?? AttesterApi(clientConfiguration: clientConfiguration) } - func lookup(email: String) async throws -> RecipientWithSortedPubKeys { + func lookup(recipient: Recipient) async throws -> RecipientWithSortedPubKeys { let results: [LookupResult] = try await withThrowingTaskGroup(of: LookupResult.self) { tg in var results: [LookupResult] = [] tg.addTask { - LookupResult(keys: try await self.wkd.lookup(email: email), source: .wkd) + LookupResult(keys: try await self.wkd.lookup(email: recipient.email), source: .wkd) } tg.addTask { - LookupResult(keys: try await self.attesterApi.lookup(email: email), source: .attester) + LookupResult(keys: try await self.attesterApi.lookup(email: recipient.email), source: .attester) } for try await result in tg { results.append(result) @@ -60,15 +60,15 @@ class PubLookup: PubLookupType { if !wkdResult.keys.isEmpty { // WKD keys are preferred. The trust level is higher because the recipient // controls the distribution of the keys themselves on their own domain - return RecipientWithSortedPubKeys(email: email, keyDetails: wkdResult.keys) + return RecipientWithSortedPubKeys(recipient, keyDetails: wkdResult.keys) } // Attester keys are less preferred because they come from less trustworthy source // (the FlowCrypt server) - return RecipientWithSortedPubKeys(email: email, keyDetails: attesterResult.keys) + return RecipientWithSortedPubKeys(recipient, keyDetails: attesterResult.keys) } - func fetchRemoteUpdateLocal(with email: String) async throws -> RecipientWithSortedPubKeys { - let recipient = try await self.lookup(email: email) + func fetchRemoteUpdateLocal(with recipient: Recipient) async throws -> RecipientWithSortedPubKeys { + let recipient = try await self.lookup(recipient: recipient) try localContactsProvider.updateKeys(for: recipient) return recipient } diff --git a/FlowCrypt/Models/Common/Recipient.swift b/FlowCrypt/Models/Common/Recipient.swift index d4ebf5f34..af175bcd3 100644 --- a/FlowCrypt/Models/Common/Recipient.swift +++ b/FlowCrypt/Models/Common/Recipient.swift @@ -7,12 +7,14 @@ // import Foundation +import MailCore +import GoogleAPIClientForREST_PeopleService -struct Recipient { - var email: String - var name: String? +struct Recipient: RecipientBase { + let email: String + let name: String? var lastUsed: Date? - var pubKeys: [PubKey] + var pubKeys: [PubKey] = [] } extension Recipient { @@ -22,4 +24,60 @@ extension Recipient { self.lastUsed = recipientObject.lastUsed self.pubKeys = recipientObject.pubKeys.map(PubKey.init) } + + init(_ string: String) { + guard let address = MCOAddress.init(nonEncodedRFC822String: string) else { + self.name = nil + self.email = string + return + } + self.name = address.displayName + self.email = address.mailbox + } + + init?(person: GTLRPeopleService_Person) { + guard let email = person.emailAddresses?.first?.value else { return nil } + + self.email = email + + if let name = person.names?.first { + self.name = [name.givenName, name.familyName].compactMap { $0 }.joined(separator: " ") + } else { + self.name = nil + } + } + + init(recipient: RecipientBase) { + self.email = recipient.email + self.name = recipient.name + } + + init(email: String, name: String? = nil) { + self.email = email + self.name = name + } +} + +extension Recipient { + var rawString: (String?, String) { (name, email) } +} + +extension Recipient: Comparable { + static func < (lhs: Recipient, rhs: Recipient) -> Bool { + guard let name1 = lhs.name else { return false } + guard let name2 = rhs.name else { return true } + return name1 < name2 + } +} + +extension Recipient: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(email) + } +} + +extension Recipient: Equatable { + static func == (lhs: Recipient, rhs: Recipient) -> Bool { + lhs.email == rhs.email + } } diff --git a/FlowCrypt/Models/Common/RecipientBase.swift b/FlowCrypt/Models/Common/RecipientBase.swift new file mode 100644 index 000000000..a801ccb8d --- /dev/null +++ b/FlowCrypt/Models/Common/RecipientBase.swift @@ -0,0 +1,31 @@ +// +// RecipientBase.swift +// FlowCrypt +// +// Created by Roma Sosnovsky on 17/02/22 +// Copyright © 2017-present FlowCrypt a. s. All rights reserved. +// + +import Foundation + +protocol RecipientBase { + var email: String { get } + var name: String? { get } +} + +extension RecipientBase { + var formatted: String { + guard let name = name else { return email } + return "\(name) <\(email)>" + } + + var shortName: String { + name?.components(separatedBy: " ").first ?? + email.components(separatedBy: "@").first ?? + "unknown" + } + + var displayName: String { + name ?? email + } +} diff --git a/FlowCryptAppTests/Functionality/Services/ComposeMessageServiceTests.swift b/FlowCryptAppTests/Functionality/Services/ComposeMessageServiceTests.swift index 8ca9b761a..21a9008e8 100644 --- a/FlowCryptAppTests/Functionality/Services/ComposeMessageServiceTests.swift +++ b/FlowCryptAppTests/Functionality/Services/ComposeMessageServiceTests.swift @@ -16,9 +16,9 @@ class ComposeMessageServiceTests: XCTestCase { var sut: ComposeMessageService! let recipients: [ComposeMessageRecipient] = [ - ComposeMessageRecipient(email: "test@gmail.com", type: .to, state: recipientIdleState), - ComposeMessageRecipient(email: "test2@gmail.com", type: .to, state: recipientIdleState), - ComposeMessageRecipient(email: "test3@gmail.com", type: .to, state: recipientIdleState) + ComposeMessageRecipient(email: "test@gmail.com", name: "Test", type: .to, state: recipientIdleState), + ComposeMessageRecipient(email: "test2@gmail.com", name: nil, type: .to, state: recipientIdleState), + ComposeMessageRecipient(email: "test3@gmail.com", name: nil, type: .to, state: recipientIdleState) ] let validKeyDetails = EncryptedStorageMock.createFakeKeyDetails(expiration: nil) let keypair = Keypair( @@ -77,9 +77,9 @@ class ComposeMessageServiceTests: XCTestCase { func testValidateMessageInputWithWhitespaceRecipients() async { let recipients: [ComposeMessageRecipient] = [ - ComposeMessageRecipient(email: " ", type: .to, state: recipientIdleState), - ComposeMessageRecipient(email: " ", type: .to, state: recipientIdleState), - ComposeMessageRecipient(email: "sdfff", type: .to, state: recipientIdleState) + ComposeMessageRecipient(email: " ", name: nil, type: .to, state: recipientIdleState), + ComposeMessageRecipient(email: " ", name: nil, type: .to, state: recipientIdleState), + ComposeMessageRecipient(email: "sdfff", name: nil, type: .to, state: recipientIdleState) ] do { _ = try await sut.validateAndProduceSendableMsg( @@ -323,7 +323,7 @@ class ComposeMessageServiceTests: XCTestCase { let expected = SendableMsg( text: message, html: nil, - to: recipients.map(\.email), + to: recipients.map(\.formatted), cc: [], bcc: [], from: email, @@ -396,7 +396,7 @@ class ComposeMessageServiceTests: XCTestCase { let expected = SendableMsg( text: message, html: nil, - to: recipients.map(\.email), + to: recipients.map(\.formatted), cc: [], bcc: [], from: email, diff --git a/FlowCryptAppTests/Functionality/Services/Key Services/Models/RecipientTests.swift b/FlowCryptAppTests/Functionality/Services/Key Services/Models/RecipientTests.swift index 4c47ecec6..7a11b142b 100644 --- a/FlowCryptAppTests/Functionality/Services/Key Services/Models/RecipientTests.swift +++ b/FlowCryptAppTests/Functionality/Services/Key Services/Models/RecipientTests.swift @@ -13,34 +13,31 @@ class RecipientTests: XCTestCase { private let calendar = Calendar.current func testRecipientWithRevokedKey() { - let keyDetails = EncryptedStorageMock.createFakeKeyDetails(expiration: nil, revoked: true) - let recipient = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", keyDetails: [keyDetails]) - + let recipient = createRecipient(keyExpiration: nil, isKeyRevoked: true) XCTAssertEqual(recipient.keyState, .revoked) } func testRecipientWithExpiredKey() { let expiration = Date().timeIntervalSince1970 - 60 * 60 - let keyDetails = EncryptedStorageMock.createFakeKeyDetails(expiration: Int(expiration)) - - let recipient = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", keyDetails: [keyDetails]) + let recipient = createRecipient(keyExpiration: Int(expiration)) XCTAssertEqual(recipient.keyState, .expired) } func testRecipientWithValidKey() { let expiration = Date().timeIntervalSince1970 + 60 * 60 - let keyDetails = EncryptedStorageMock.createFakeKeyDetails(expiration: Int(expiration)) - let recipient = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", keyDetails: [keyDetails]) + let recipient = createRecipient(keyExpiration: Int(expiration)) XCTAssertEqual(recipient.keyState, .active) XCTAssertEqual(recipient.pubKeys.first?.emails, ["test@flowcrypt.com"]) - let keyDetails2 = EncryptedStorageMock.createFakeKeyDetails(expiration: nil) - let recipient2 = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", keyDetails: [keyDetails2]) + let recipient2 = createRecipient(keyExpiration: nil) XCTAssertEqual(recipient2.keyState, .active) } func testRecipientWithoutPubKey() { - let recipient = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", keyDetails: []) + let recipient = RecipientWithSortedPubKeys( + Recipient(email: "test@flowcrypt.com"), + keyDetails: [] + ) XCTAssertEqual(recipient.keyState, .empty) } @@ -57,8 +54,10 @@ class RecipientTests: XCTestCase { let oldExpiredKey = EncryptedStorageMock.createFakeKeyDetails(expiration: now - 2 * 3600) let keyDetails = [revokedKey, oldExpiredKey, activeKey1, expiredKey, activeKey2, nonExpiringKey, activeKey3] - let recipient = RecipientWithSortedPubKeys(email: "test@flowcrypt.com", - keyDetails: keyDetails) + let recipient = RecipientWithSortedPubKeys( + Recipient(email: "test@flowcrypt.com"), + keyDetails: keyDetails + ) XCTAssertEqual(recipient.pubKeys[0].fingerprint, nonExpiringKey.primaryFingerprint) XCTAssertEqual(recipient.pubKeys[1].fingerprint, activeKey3.primaryFingerprint) @@ -68,4 +67,13 @@ class RecipientTests: XCTestCase { XCTAssertEqual(recipient.pubKeys[5].fingerprint, oldExpiredKey.primaryFingerprint) XCTAssertEqual(recipient.pubKeys[6].fingerprint, revokedKey.primaryFingerprint) } + + private func createRecipient(keyExpiration: Int?, isKeyRevoked: Bool = false) -> RecipientWithSortedPubKeys { + let keyDetails = EncryptedStorageMock.createFakeKeyDetails( + expiration: keyExpiration, + revoked: isKeyRevoked + ) + let recipient = Recipient(email: "test@flowcrypt.com") + return RecipientWithSortedPubKeys(recipient, keyDetails: [keyDetails]) + } } diff --git a/FlowCryptAppTests/Functionality/Services/Key Services/PubLookupTest.swift b/FlowCryptAppTests/Functionality/Services/Key Services/PubLookupTest.swift index 6c6e3aa36..829eea20b 100644 --- a/FlowCryptAppTests/Functionality/Services/Key Services/PubLookupTest.swift +++ b/FlowCryptAppTests/Functionality/Services/Key Services/PubLookupTest.swift @@ -21,7 +21,7 @@ class PubLookupTest: XCTestCase { ), localContactsProvider: LocalContactsProviderMock() ) - let r = try await pubLookup.lookup(email: "different.uid@recipient.test") + let r = try await pubLookup.lookup(recipient: Recipient(email: "different.uid@recipient.test")) XCTAssertTrue(r.pubKeys.isNotEmpty, "expected pubkeys not empty") XCTAssertEqual(r.pubKeys.first?.longid, "0C9C2E6A4D273C6F") } diff --git a/FlowCryptAppTests/Mocks/LocalContactsProviderMock.swift b/FlowCryptAppTests/Mocks/LocalContactsProviderMock.swift index 33572c48f..3706d6c6a 100644 --- a/FlowCryptAppTests/Mocks/LocalContactsProviderMock.swift +++ b/FlowCryptAppTests/Mocks/LocalContactsProviderMock.swift @@ -13,7 +13,7 @@ final class LocalContactsProviderMock: LocalContactsProviderType { func searchRecipient(with email: String) async throws -> RecipientWithSortedPubKeys? { nil } - func searchEmails(query: String) throws -> [String] { [] } + func searchRecipients(query: String) throws -> [Recipient] { [] } func save(recipient: RecipientWithSortedPubKeys) throws {} diff --git a/FlowCryptUI/Cell Nodes/LabelCellNode.swift b/FlowCryptUI/Cell Nodes/LabelCellNode.swift index 372afbda8..9917f2606 100644 --- a/FlowCryptUI/Cell Nodes/LabelCellNode.swift +++ b/FlowCryptUI/Cell Nodes/LabelCellNode.swift @@ -14,17 +14,20 @@ public final class LabelCellNode: CellNode { let title: NSAttributedString let text: NSAttributedString let insets: UIEdgeInsets + let spacing: CGFloat let accessibilityIdentifier: String? public init( title: NSAttributedString, text: NSAttributedString, insets: UIEdgeInsets = .deviceSpecificTextInsets(top: 8, bottom: 8), - accessibilityIdentifier: String? + spacing: CGFloat = 4, + accessibilityIdentifier: String? = nil ) { self.title = title self.text = text self.insets = insets + self.spacing = spacing self.accessibilityIdentifier = accessibilityIdentifier } } @@ -47,7 +50,7 @@ public final class LabelCellNode: CellNode { insets: input.insets, child: ASStackLayoutSpec( direction: .vertical, - spacing: 4, + spacing: input.spacing, justifyContent: .start, alignItems: .start, children: [titleNode, textNode] diff --git a/Gemfile.lock b/Gemfile.lock index 7cf828032..611c040a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,17 +17,17 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.555.0) - aws-sdk-core (3.126.2) + aws-partitions (1.559.0) + aws-sdk-core (3.127.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.54.0) - aws-sdk-core (~> 3, >= 3.126.0) + aws-sdk-kms (1.55.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.112.0) - aws-sdk-core (~> 3, >= 3.126.0) + aws-sdk-s3 (1.113.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) aws-sigv4 (1.4.0) @@ -191,8 +191,8 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.1.1) - faraday (>= 0.17.3, < 2.0) + googleauth (1.1.2) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) @@ -243,9 +243,9 @@ GEM ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.16.0) + signet (0.16.1) addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) + faraday (>= 0.17.5, < 3.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simctl (1.6.8) diff --git a/Podfile.lock b/Podfile.lock index d9907e738..039a2bd8e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,7 +15,7 @@ PODS: - PINRemoteImage/PINCache (3.0.3): - PINCache (~> 3.0.3) - PINRemoteImage/Core - - SwiftLint (0.46.2) + - SwiftLint (0.46.3) - SwiftyRSA (1.7.0): - SwiftyRSA/ObjC (= 1.7.0) - SwiftyRSA/ObjC (1.7.0) @@ -61,7 +61,7 @@ SPEC CHECKSUMS: PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086 PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20 PINRemoteImage: f1295b29f8c5e640e25335a1b2bd9d805171bd01 - SwiftLint: 6bc52a21f0fd44cab9aa2dc8e534fb9f5e3ec507 + SwiftLint: ae76d82f056734f853c8cd0371503252c606c482 SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6 Texture: 2e8ab2519452515f7f5a520f5a8f7e0a413abfa3 diff --git a/appium/tests/data/index.ts b/appium/tests/data/index.ts index 1c505351d..d98ddbe36 100644 --- a/appium/tests/data/index.ts +++ b/appium/tests/data/index.ts @@ -23,6 +23,7 @@ export const CommonData = { threadMessage: { subject: 'test thread rendering', sender: 'dmitry@flowcrypt.com', + senderName: 'Dmitry at FlowCrypt', firstThreadMessage: 'first message', secondThreadMessage: 'Second thread rendering message\n' + '\n' + @@ -41,10 +42,11 @@ export const CommonData = { }, sender: { email: 'dmitry@flowcrypt.com', + name: 'Dmitry at FlowCrypt' }, contact: { email: 'dmitry@flowcrypt.com', - name: 'Dima' + name: 'Dima Flowcrypt' }, secondContact: { email: 'demo@flowcrypt.com', @@ -68,6 +70,7 @@ export const CommonData = { }, emailWithMultipleRecipientsWithCC: { sender: 'ioan@flowcrypt.com', + senderName: 'Ioan at FlowCrypt', recipient: 'robot@flowcrypt.com', cc: 'robot+cc@flowcrypt.com', subject: 'Message with cc and multiple recipients and attachment', @@ -95,6 +98,7 @@ export const CommonData = { }, recipientsListEmail: { sender: 'flowcrypt.compatibility@gmail.com', + senderName: 'FlowCrypt Compatibility', subject: 'CC and BCC test', message: 'Test message for CC and BCC recipients', recipients: 'to Robot, robot+cc, e2e.enterprise.test', @@ -111,11 +115,13 @@ export const CommonData = { subject: 'message encrypted for another public key (only one pubkey used)', message: 'key_mismatch: Missing appropriate key', senderEmail: 'flowcrypt.compatibility@gmail.com', + senderName: 'FlowCrypt Compatibility' }, wrongChecksumEmail: { subject: 'wrong checksum', message: 'format: Error: Ascii armor integrity check on message failed: \'FdCC\' should be \'FddK\'', senderEmail: 'flowcrypt.compatibility@gmail.com', + senderName: 'FlowCrypt Compatibility' }, notIntegrityProtected: { subject: 'not integrity protected - should show a warning and not decrypt automatically', @@ -126,6 +132,7 @@ export const CommonData = { subject: 'key mismatch unexpectedly produces a modal', message: 'Here are the images for testing compatibility.', senderEmail: 'sunitnandi834@gmail.com', + senderName: 'Sunit Kumar Nandi', encryptedBadgeText: 'encrypted', signatureBadgeText: 'not signed', firstAttachmentName: 'Screenshot_20180422_125217.png.asc', diff --git a/appium/tests/helpers/PublicKeyHelper.ts b/appium/tests/helpers/PublicKeyHelper.ts index 94cb6f879..b06f07ca1 100644 --- a/appium/tests/helpers/PublicKeyHelper.ts +++ b/appium/tests/helpers/PublicKeyHelper.ts @@ -39,7 +39,7 @@ class PublicKeyHelper { // Add first contact await MailFolderScreen.clickCreateEmail(); await NewMessageScreen.setAddRecipientByName(userName, userEmail); - await NewMessageScreen.checkAddedRecipientColor(userEmail, 0, 'green'); + await NewMessageScreen.checkAddedRecipientColor(userName, 0, 'green'); await NewMessageScreen.clickBackButton(); // Go to Contacts screen diff --git a/appium/tests/screenobjects/contact-public-key.screen.ts b/appium/tests/screenobjects/contact-public-key.screen.ts index 5796deafe..36caba2a6 100644 --- a/appium/tests/screenobjects/contact-public-key.screen.ts +++ b/appium/tests/screenobjects/contact-public-key.screen.ts @@ -77,10 +77,11 @@ class ContactPublicKeyScreen extends BaseScreen { await ElementHelper.waitElementInvisible(await this.expiresValue); } - checkPgpUserId = async (email: string) => { + checkPgpUserId = async (email: string, name?: string) => { + const value = name ? `${name} <${email}>` : email; await (await this.trashButton).waitForDisplayed(); await (await this.pgpUserIdLabel).waitForDisplayed(); - expect(await (await this.pgpUserIdEmailValue).getAttribute('value')).toContain(email); + expect(await (await this.pgpUserIdEmailValue).getAttribute('value')).toContain(value); } clickOnFingerPrint = async () => { diff --git a/appium/tests/screenobjects/email.screen.ts b/appium/tests/screenobjects/email.screen.ts index a8d088b41..4c4f52ace 100644 --- a/appium/tests/screenobjects/email.screen.ts +++ b/appium/tests/screenobjects/email.screen.ts @@ -118,14 +118,14 @@ class EmailScreen extends BaseScreen { return $(SELECTORS.ATTACHMENT_TEXT_VIEW); } - senderEmail = async (index = 0) =>{ - return $(`~aid-sender-${index}`) - } - - checkEmailAddress = async (email: string, index = 0)=> { + checkEmailSender = async (sender: string, index = 0) => { const element = await this.senderEmail(index); await (await element).waitForDisplayed(); - await expect(await (await element).getValue()).toEqual(email); + await expect(await (await element).getValue()).toEqual(sender); + } + + senderEmail = async (index = 0) =>{ + return $(`~aid-sender-${index}`) } checkEmailSubject = async (subject: string) => { @@ -142,14 +142,14 @@ class EmailScreen extends BaseScreen { } checkOpenedEmail = async (email: string, subject: string, text: string) => { - await this.checkEmailAddress(email); + await this.checkEmailSender(email); await this.checkEmailSubject(subject); await this.checkEmailText(text); } checkThreadMessage = async (email: string, subject: string, text: string, date: string, index = 0) => { await this.checkEmailSubject(subject); - await this.checkEmailAddress(email, index); + await this.checkEmailSender(email, index); await this.clickExpandButtonByIndex(index); await this.checkEmailText(text, index); await this.checkDate(date, index); diff --git a/appium/tests/screenobjects/mail-folder.screen.ts b/appium/tests/screenobjects/mail-folder.screen.ts index 225bbaf2c..532af1813 100644 --- a/appium/tests/screenobjects/mail-folder.screen.ts +++ b/appium/tests/screenobjects/mail-folder.screen.ts @@ -72,7 +72,7 @@ class MailFolderScreen extends BaseScreen { clickOnEmailBySubject = async (subject: string) => { const selector = `~${subject}`; - if (await (await $(selector)).isDisplayed() !== true) { + if (!await (await $(selector)).isDisplayed()) { await TouchHelper.scrollDownToElement(await $(selector)); } await ElementHelper.waitAndClick(await $(selector), 500); diff --git a/appium/tests/specs/live/composeEmail/CheckRecipientColorAfterRemovingPubKey.spec.ts b/appium/tests/specs/live/composeEmail/CheckRecipientColorAfterRemovingPubKey.spec.ts index da9e94f29..49f964913 100644 --- a/appium/tests/specs/live/composeEmail/CheckRecipientColorAfterRemovingPubKey.spec.ts +++ b/appium/tests/specs/live/composeEmail/CheckRecipientColorAfterRemovingPubKey.spec.ts @@ -26,7 +26,7 @@ describe('COMPOSE EMAIL: ', () => { await PublicKeyHelper.addRecipientAndCheckFetchedKey(contactName, contactEmail); await PublicKeyDetailsScreen.clickTrashButton(); - await ContactPublicKeyScreen.checkPgpUserId(contactEmail); + await ContactPublicKeyScreen.checkPgpUserId(contactEmail, contactName); await ContactPublicKeyScreen.checkPublicKeyDetailsNotDisplayed(); await ContactPublicKeyScreen.clickBackButton(); diff --git a/appium/tests/specs/live/composeEmail/SelectRecipientByName.spec.ts b/appium/tests/specs/live/composeEmail/SelectRecipientByName.spec.ts index e21d93a71..a5d9a947c 100644 --- a/appium/tests/specs/live/composeEmail/SelectRecipientByName.spec.ts +++ b/appium/tests/specs/live/composeEmail/SelectRecipientByName.spec.ts @@ -18,11 +18,9 @@ describe('COMPOSE EMAIL: ', () => { const firstContactEmail = CommonData.contact.email; const firstContactName = CommonData.contact.name; - const firstContactItemName = 'Dmitry at FlowCrypt'; const secondContactEmail = CommonData.secondContact.email; const secondContactName = CommonData.secondContact.name; - const secondContactItemName = 'Demo key 2'; await SplashScreen.login(); await SetupKeyScreen.setPassPhrase(); @@ -47,13 +45,13 @@ describe('COMPOSE EMAIL: ', () => { // Add first contact await MailFolderScreen.clickCreateEmail(); await NewMessageScreen.setAddRecipientByName(firstContactName, firstContactEmail); - await NewMessageScreen.checkAddedRecipient(firstContactEmail); + await NewMessageScreen.checkAddedRecipient(firstContactName); await NewMessageScreen.clickBackButton(); // Add second contact await MailFolderScreen.clickCreateEmail(); await NewMessageScreen.setAddRecipientByName(secondContactName, secondContactEmail); - await NewMessageScreen.checkAddedRecipient(secondContactEmail); + await NewMessageScreen.checkAddedRecipient(secondContactName); await NewMessageScreen.clickBackButton(); // Go to Contacts screen @@ -65,13 +63,13 @@ describe('COMPOSE EMAIL: ', () => { await SettingsScreen.clickOnSettingItem('Contacts'); await ContactScreen.checkContactScreen(); - await ContactScreen.checkContact(firstContactItemName); - await ContactScreen.checkContact(secondContactItemName); + await ContactScreen.checkContact(firstContactName); + await ContactScreen.checkContact(secondContactName); // Go to Contact screen - await ContactScreen.clickOnContact(firstContactItemName); + await ContactScreen.clickOnContact(firstContactName); - await ContactPublicKeyScreen.checkPgpUserId(firstContactEmail); + await ContactPublicKeyScreen.checkPgpUserId(firstContactEmail, firstContactName); await ContactPublicKeyScreen.checkPublicKeyDetailsNotEmpty(); await ContactPublicKeyScreen.clickOnFingerPrint(); await PublicKeyDetailsScreen.checkPublicKeyDetailsScreen(); diff --git a/appium/tests/specs/live/inbox/CheckEncryptedEmailAfterRestartApp.spec.ts b/appium/tests/specs/live/inbox/CheckEncryptedEmailAfterRestartApp.spec.ts index 396abd5c4..b8ee2c16c 100644 --- a/appium/tests/specs/live/inbox/CheckEncryptedEmailAfterRestartApp.spec.ts +++ b/appium/tests/specs/live/inbox/CheckEncryptedEmailAfterRestartApp.spec.ts @@ -12,7 +12,7 @@ describe('INBOX: ', () => { it('user is able to see encrypted email with pass phrase after restart app', async () => { - const senderEmail = CommonData.sender.email; + const senderName = CommonData.sender.name; const emailSubject = CommonData.encryptedEmail.subject; const emailText = CommonData.encryptedEmail.message; const wrongPassPhrase = 'wrong'; @@ -27,7 +27,7 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await driver.terminateApp(bundleId); await driver.activateApp(bundleId); @@ -44,11 +44,11 @@ describe('INBOX: ', () => { //check email after setting correct pass phrase await EmailScreen.enterPassPhrase(correctPassPhrase); await EmailScreen.clickOkButton(); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); //reopen email without pass phrase await EmailScreen.clickBackButton(); await MailFolderScreen.clickOnEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); }); }); diff --git a/appium/tests/specs/live/inbox/CheckMessageProcessingErrors.spec.ts b/appium/tests/specs/live/inbox/CheckMessageProcessingErrors.spec.ts index 842871ae6..5b8daad70 100644 --- a/appium/tests/specs/live/inbox/CheckMessageProcessingErrors.spec.ts +++ b/appium/tests/specs/live/inbox/CheckMessageProcessingErrors.spec.ts @@ -22,12 +22,12 @@ describe('INBOX: ', () => { // Const for message encrypted for another public key const encryptedForAnotherPublicKeySubject = CommonData.encryptedForAnotherPublicKeyEmail.subject; - const encryptedForAnotherPublicKeyEmail = CommonData.encryptedForAnotherPublicKeyEmail.senderEmail; + const encryptedForAnotherPublicKeyName = CommonData.encryptedForAnotherPublicKeyEmail.senderName; const encryptedForAnotherPublicKeyText = CommonData.encryptedForAnotherPublicKeyEmail.message; // Const for encrypted for a wrong checksum message const wrongChecksumSubject = CommonData.wrongChecksumEmail.subject; - const wrongChecksumEmail = CommonData.wrongChecksumEmail.senderEmail; + const wrongChecksumName = CommonData.wrongChecksumEmail.senderName; const wrongChecksumText = CommonData.wrongChecksumEmail.message; const notIntegrityProtectedSubject = CommonData.notIntegrityProtected.subject; @@ -35,7 +35,7 @@ describe('INBOX: ', () => { const notIntegrityProtectedText = CommonData.notIntegrityProtected.message; const keyMismatchSubject = CommonData.keyMismatch.subject; - const keyMismatchEmail = CommonData.keyMismatch.senderEmail; + const keyMismatchName = CommonData.keyMismatch.senderName; const keyMismatchText = CommonData.keyMismatch.message; const keyMismatchEncryptedBadge = CommonData.keyMismatch.encryptedBadgeText; const keyMismatchSignatureBadge= CommonData.keyMismatch.signatureBadgeText; @@ -60,7 +60,7 @@ describe('INBOX: ', () => { // Checking error message encrypted for another public key await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(encryptedForAnotherPublicKeySubject); - await EmailScreen.checkOpenedEmail(encryptedForAnotherPublicKeyEmail, encryptedForAnotherPublicKeySubject, encryptedForAnotherPublicKeyText); + await EmailScreen.checkOpenedEmail(encryptedForAnotherPublicKeyName, encryptedForAnotherPublicKeySubject, encryptedForAnotherPublicKeyText); await EmailScreen.checkEncryptionBadge(decryptErrorBadgeText); await EmailScreen.clickBackButton(); @@ -70,7 +70,7 @@ describe('INBOX: ', () => { // Checking error for wrong checksum message await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(wrongChecksumSubject); - await EmailScreen.checkOpenedEmail(wrongChecksumEmail, wrongChecksumSubject, wrongChecksumText); + await EmailScreen.checkOpenedEmail(wrongChecksumName, wrongChecksumSubject, wrongChecksumText); await EmailScreen.checkEncryptionBadge(decryptErrorBadgeText); // Checking error for integrity protected message @@ -92,7 +92,7 @@ describe('INBOX: ', () => { await SearchScreen.searchAndClickEmailBySubject(keyMismatchSubject); await MailFolderScreen.clickOnEmailBySubject(keyMismatchSubject); - await EmailScreen.checkOpenedEmail(keyMismatchEmail, keyMismatchSubject, keyMismatchText); + await EmailScreen.checkOpenedEmail(keyMismatchName, keyMismatchSubject, keyMismatchText); await EmailScreen.checkEncryptionBadge(keyMismatchEncryptedBadge); await EmailScreen.checkSignatureBadge(keyMismatchSignatureBadge); await EmailScreen.checkAttachment(firstAttachmentName); diff --git a/appium/tests/specs/live/inbox/CheckReplyAndForwardForEncryptedEmail.spec.ts b/appium/tests/specs/live/inbox/CheckReplyAndForwardForEncryptedEmail.spec.ts index 1adb337e4..a080fa106 100644 --- a/appium/tests/specs/live/inbox/CheckReplyAndForwardForEncryptedEmail.spec.ts +++ b/appium/tests/specs/live/inbox/CheckReplyAndForwardForEncryptedEmail.spec.ts @@ -14,6 +14,7 @@ describe('INBOX: ', () => { it('user is able to reply or forward email and check info from composed email', async () => { const senderEmail = CommonData.emailWithMultipleRecipientsWithCC.sender; + const senderName = CommonData.emailWithMultipleRecipientsWithCC.senderName; const recipientEmail = CommonData.emailWithMultipleRecipientsWithCC.recipient; const ccEmail = CommonData.emailWithMultipleRecipientsWithCC.cc; const emailSubject = CommonData.emailWithMultipleRecipientsWithCC.subject; @@ -30,12 +31,12 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); // check reply message await EmailScreen.clickReplyButton(); await NewMessageScreen.checkFilledComposeEmailInfo({ - recipients: [senderEmail], + recipients: [senderName], subject: replySubject, message: quoteText }); @@ -45,7 +46,7 @@ describe('INBOX: ', () => { await EmailScreen.clickMenuButton(); await EmailScreen.clickReplyAllButton(); await NewMessageScreen.checkFilledComposeEmailInfo({ - recipients: [recipientEmail, senderEmail], + recipients: [recipientEmail, senderName], subject: replySubject, message: quoteText, cc: [ccEmail] diff --git a/appium/tests/specs/live/inbox/CheckThreadRendering.spec.ts b/appium/tests/specs/live/inbox/CheckThreadRendering.spec.ts index c6416acac..422bc03f6 100644 --- a/appium/tests/specs/live/inbox/CheckThreadRendering.spec.ts +++ b/appium/tests/specs/live/inbox/CheckThreadRendering.spec.ts @@ -9,7 +9,7 @@ import { CommonData } from '../../../data'; describe('INBOX: ', () => { it('check thread rendering', async () => { - const senderEmail = CommonData.threadMessage.sender; + const senderName = CommonData.threadMessage.senderName; const emailSubject = CommonData.threadMessage.subject; const firstMessage = CommonData.threadMessage.firstThreadMessage; const secondMessage = CommonData.threadMessage.secondThreadMessage; @@ -24,8 +24,8 @@ describe('INBOX: ', () => { await MailFolderScreen.checkInboxScreen(); await MailFolderScreen.clickOnEmailBySubject(emailSubject); - await EmailScreen.checkThreadMessage(senderEmail, emailSubject, thirdMessage, dateThird, 2); + await EmailScreen.checkThreadMessage(senderName, emailSubject, thirdMessage, dateThird, 2); await EmailScreen.checkThreadMessage(userEmail, emailSubject, secondMessage, dateSecond, 1); - await EmailScreen.checkThreadMessage(senderEmail, emailSubject, firstMessage, dateFirst); + await EmailScreen.checkThreadMessage(senderName, emailSubject, firstMessage, dateFirst); }); }); diff --git a/appium/tests/specs/live/inbox/ReadAttachmentEmail.spec.ts b/appium/tests/specs/live/inbox/ReadAttachmentEmail.spec.ts index c528a51d9..79cfb4556 100644 --- a/appium/tests/specs/live/inbox/ReadAttachmentEmail.spec.ts +++ b/appium/tests/specs/live/inbox/ReadAttachmentEmail.spec.ts @@ -13,7 +13,7 @@ describe('INBOX: ', () => { it('user is able to view encrypted email with attachment', async () => { - const senderEmail = CommonData.sender.email; + const senderName = CommonData.sender.name; const emailSubject = CommonData.encryptedEmailWithAttachment.subject; const emailText = CommonData.encryptedEmailWithAttachment.message; const attachmentName = CommonData.encryptedEmailWithAttachment.attachmentName; @@ -29,7 +29,7 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await EmailScreen.checkAttachment(encryptedAttachmentName); await driver.terminateApp(bundleId); @@ -47,7 +47,7 @@ describe('INBOX: ', () => { //check attachment after setting correct pass phrase await EmailScreen.enterPassPhrase(correctPassPhrase); await EmailScreen.clickOkButton(); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await EmailScreen.checkAttachment(encryptedAttachmentName); await EmailScreen.clickOnAttachmentCell(); await AttachmentScreen.checkAttachment(attachmentName); @@ -63,7 +63,7 @@ describe('INBOX: ', () => { await EmailScreen.clickBackButton(); await MailFolderScreen.clickOnEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await EmailScreen.checkAttachment(encryptedAttachmentName); await EmailScreen.clickOnAttachmentCell(); diff --git a/appium/tests/specs/live/inbox/ReadEmailAfterRestartApp.spec.ts b/appium/tests/specs/live/inbox/ReadEmailAfterRestartApp.spec.ts index a871e2040..6da810990 100644 --- a/appium/tests/specs/live/inbox/ReadEmailAfterRestartApp.spec.ts +++ b/appium/tests/specs/live/inbox/ReadEmailAfterRestartApp.spec.ts @@ -12,7 +12,7 @@ describe('INBOX: ', () => { it('user is able to see plain email without setting pass phrase after restart app', async () => { - const senderEmail = CommonData.sender.email; + const senderName = CommonData.sender.name; const emailSubject = CommonData.simpleEmail.subject; const emailText = CommonData.simpleEmail.message; @@ -22,7 +22,7 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await driver.terminateApp(CommonData.bundleId.id); await driver.activateApp(CommonData.bundleId.id); @@ -31,6 +31,6 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); }); }); diff --git a/appium/tests/specs/live/inbox/ReadTextEmail.spec.ts b/appium/tests/specs/live/inbox/ReadTextEmail.spec.ts index 642341ef0..184fe231d 100644 --- a/appium/tests/specs/live/inbox/ReadTextEmail.spec.ts +++ b/appium/tests/specs/live/inbox/ReadTextEmail.spec.ts @@ -12,7 +12,7 @@ describe('INBOX: ', () => { it('user is able to view text email and recipients list', async () => { - const senderEmail = CommonData.recipientsListEmail.sender; + const senderName = CommonData.recipientsListEmail.senderName; const emailSubject = CommonData.recipientsListEmail.subject; const emailText = CommonData.recipientsListEmail.message; const recipientsButton = CommonData.recipientsListEmail.recipients; @@ -27,7 +27,7 @@ describe('INBOX: ', () => { await MailFolderScreen.clickSearchButton(); await SearchScreen.searchAndClickEmailBySubject(emailSubject); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); await EmailScreen.checkRecipientsButton(recipientsButton); await EmailScreen.clickRecipientsButton(); await EmailScreen.checkRecipientsList(toLabel, ccLabel, bccLabel); diff --git a/appium/tests/specs/live/update/CheckAppAfterUpdateFromOldVersion.spec.ts b/appium/tests/specs/live/update/CheckAppAfterUpdateFromOldVersion.spec.ts index 11789c98e..014166ce4 100644 --- a/appium/tests/specs/live/update/CheckAppAfterUpdateFromOldVersion.spec.ts +++ b/appium/tests/specs/live/update/CheckAppAfterUpdateFromOldVersion.spec.ts @@ -26,6 +26,7 @@ describe('UPDATE: ', () => { const firstContactItemName = 'Dmitry at FlowCrypt'; const firstContactEmail = CommonData.contact.email; const senderEmail = CommonData.sender.email; + const senderName = CommonData.sender.name; const emailSubject = CommonData.encryptedEmail.subject; const emailText = CommonData.encryptedEmail.message; @@ -124,6 +125,6 @@ describe('UPDATE: ', () => { await SearchScreen.searchAndClickEmailBySubject(emailSubject); await EmailScreen.enterPassPhrase(correctPassPhrase); await EmailScreen.clickOkButton(); - await EmailScreen.checkOpenedEmail(senderEmail, emailSubject, emailText); + await EmailScreen.checkOpenedEmail(senderName, emailSubject, emailText); }); });