From 661a61da92054449c5f13f1c6ab7b04337f03326 Mon Sep 17 00:00:00 2001 From: QSD_e Date: Wed, 28 Apr 2021 10:35:56 +0200 Subject: [PATCH 1/8] issue #193 add attachment nodes --- FlowCrypt.xcodeproj/project.pbxproj | 6 +- .../Msg/MessageViewController.swift | 11 ++- FlowCrypt/Core/CoreTypes.swift | 2 +- FlowCryptUI/Nodes/AttachmentNode.swift | 99 +++++++++++++++++++ 4 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 FlowCryptUI/Nodes/AttachmentNode.swift diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 25433beaf..a69e4411d 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 32DCAF683D87EA6221F71335 /* SequenceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA058652FD4616FB04FB6 /* SequenceExtensions.swift */; }; 32DCAF95A6A329C3136B1C8E /* Imap+msg.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA55C094E9745AA1FD210 /* Imap+msg.swift */; }; 32DCAF9DA9EC47798DF8BB73 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA9701B2D5052225A0414 /* SignInViewController.swift */; }; + 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 5A39F42D239EC321001F4607 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42C239EC321001F4607 /* SettingsViewController.swift */; }; 5A39F430239EC396001F4607 /* SettingsViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42F239EC396001F4607 /* SettingsViewDecorator.swift */; }; 5A39F437239ECC23001F4607 /* KeySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F436239ECC23001F4607 /* KeySettingsViewController.swift */; }; @@ -132,7 +133,6 @@ 9FE1B3802563F85400D6D086 /* MessagesListProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE1B37F2563F85400D6D086 /* MessagesListProvider.swift */; }; 9FE1B3942563F98600D6D086 /* Imap+MessagesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE1B3932563F98600D6D086 /* Imap+MessagesList.swift */; }; 9FE1B3A02565B0CE00D6D086 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE1B39F2565B0CD00D6D086 /* Message.swift */; }; - 9FE233D825712E51003D7C7E /* test-ci-secrets.json in Resources */ = {isa = PBXBuildFile; fileRef = 9FE233D725712E51003D7C7E /* test-ci-secrets.json */; }; 9FE743072347AA54005E2DBB /* MainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE743062347AA54005E2DBB /* MainNavigationController.swift */; }; 9FEED1D2230DAD1E00700F8E /* InboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FEED1D1230DAD1E00700F8E /* InboxViewModel.swift */; }; 9FF0670825520CF800FCC9E6 /* GmailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF0670725520CF800FCC9E6 /* GmailService.swift */; }; @@ -368,6 +368,7 @@ 411CEC75050F852F172CD687 /* Pods-FlowCryptTests.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlowCryptTests.testflight.xcconfig"; path = "Target Support Files/Pods-FlowCryptTests/Pods-FlowCryptTests.testflight.xcconfig"; sourceTree = ""; }; 44D0BF0D60EF854CEC17561C /* Pods-FlowCryptUIApplication.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlowCryptUIApplication.testflight.xcconfig"; path = "Target Support Files/Pods-FlowCryptUIApplication/Pods-FlowCryptUIApplication.testflight.xcconfig"; sourceTree = ""; }; 4A76C3D4559C9F415D392A62 /* Pods-FlowCryptTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlowCryptTests.debug.xcconfig"; path = "Target Support Files/Pods-FlowCryptTests/Pods-FlowCryptTests.debug.xcconfig"; sourceTree = ""; }; + 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentNode.swift; sourceTree = ""; }; 567BA6739257FE0D2924D82C /* Pods_FlowCryptUIApplication.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FlowCryptUIApplication.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5A39F42C239EC321001F4607 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 5A39F42F239EC396001F4607 /* SettingsViewDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewDecorator.swift; sourceTree = ""; }; @@ -1578,6 +1579,7 @@ 9F4453BF236B894D005D7D05 /* TableNode.swift */, 9F4453C1236B9273005D7D05 /* TextFieldNode.swift */, D24FAFAA2520BFAE00BF46C5 /* CheckBoxNode.swift */, + 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */, ); path = Nodes; sourceTree = ""; @@ -1854,7 +1856,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9FE233D825712E51003D7C7E /* test-ci-secrets.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2406,6 +2407,7 @@ D28655912423B4580066F52E /* HeaderNode.swift in Sources */, D271774C242558DA00BDA9A9 /* MessageTextSubjectNode.swift in Sources */, D27177502425659F00BDA9A9 /* SettingsCellNode.swift in Sources */, + 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */, D2CDC3D824047066002B045F /* RecipientEmailNode.swift in Sources */, D2717753242568A600BDA9A9 /* NavigationBarItemsView.swift in Sources */, D211CE7023FC35AC00D1CE38 /* TableNode.swift in Sources */, diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index e073f26d5..34b33813a 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -14,7 +14,7 @@ final class MessageViewController: TableNodeViewController { } enum Parts: Int, CaseIterable { - case sender, subject, text + case sender, subject, text, attachment var indexPath: IndexPath { IndexPath(row: rawValue, section: 0) @@ -171,6 +171,10 @@ extension MessageViewController { ) let decryptErrBlocks = decrypted.blocks.filter { $0.decryptErr != nil } + decrypted.blocks.forEach { block in + // + } + let message: NSAttributedString if let decryptErrBlock = decryptErrBlocks.first { let rawMsg = decryptErrBlock.content @@ -366,6 +370,9 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { date: input?.objMessage.date ) + let input = AttachmentsNode.Input.init(name: "Attachment 1", size: "100 MB") + let input2 = AttachmentsNode.Input.init(name: "Attachment 2", size: "16 MB") + return { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { @@ -377,6 +384,8 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { return MessageSubjectNode(subject, time: time) case .text: return MessageTextSubjectNode(self.message) + case .attachment: + return AttachmentsNode.init(input: [input, input2]) } } } diff --git a/FlowCrypt/Core/CoreTypes.swift b/FlowCrypt/Core/CoreTypes.swift index 5b3e7c7db..90959d11f 100644 --- a/FlowCrypt/Core/CoreTypes.swift +++ b/FlowCrypt/Core/CoreTypes.swift @@ -19,7 +19,7 @@ struct CoreRes { let text: String } - struct ParseDecryptMsg { + struct ParseDecryptMsg: Decodable { let replyType: ReplyType let text: String let blocks: [MsgBlock] diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift new file mode 100644 index 000000000..b5e318255 --- /dev/null +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -0,0 +1,99 @@ +// +// AttachmentNode.swift +// FlowCryptUI +// +// Created by QSD BiH on 16. 4. 2021.. +// Copyright © 2021 FlowCrypt Limited. All rights reserved. +// + +import AsyncDisplayKit + +public struct Attachment { + var name, size: NSAttributedString +} + +public final class AttachmentsNode: CellNode { + public struct Input { + let name: String + let size: String + + public init( + name: String, + size: String + ) { + self.name = name + self.size = size + } + } + + private var attachmentNodes: [AttachmentNode] = [] + + public init(input: [Input]) { + super.init() + input.forEach { input in + attachmentNodes.append( + AttachmentNode( + input: AttachmentNode.Input(name: input.name, size: input.size) + ) + ) + } + } + + public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { + return ASInsetLayoutSpec( + insets: UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0), + child: ASStackLayoutSpec( + direction: .vertical, + spacing: 8, + justifyContent: .start, + alignItems: .stretch, + children: attachmentNodes)) + } +} + +public final class AttachmentNode: CellNode { + public struct Input { + var name, size: String + } + + private let titleNode = ASTextNode() + private let subtitleNode = ASTextNode2() + private let imageNode = ASImageNode() + private let buttonNode = ASButtonNode() + private let separatorNode = ASDisplayNode() + + public init(input: Input) { + super.init() + self.borderWidth = 0.5 + self.borderColor = UIColor(named: "red")?.cgColor + + imageNode.image = UIImage(named: "paperclip") + buttonNode.setImage(UIImage(named: "paperclip"), for: .normal) + titleNode.attributedText = NSAttributedString.text(from: input.name, style: .medium(16)) + subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12)) + } + + public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { + let verticalStack = ASStackLayoutSpec.vertical() + verticalStack.spacing = 6 + verticalStack.style.flexShrink = 1.0 + verticalStack.style.flexGrow = 1.0 + separatorNode.style.flexGrow = 1.0 + separatorNode.style.preferredSize.height = 1.0 + + verticalStack.children = [titleNode, subtitleNode] + + let finalSpec = ASStackLayoutSpec( + direction: .horizontal, + spacing: 8, + justifyContent: .start, + alignItems: .center, + children: [imageNode, verticalStack, separatorNode, buttonNode] + ) + + return ASInsetLayoutSpec( + insets: UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16), + child: finalSpec + ) + } +} From d623fd80137492117c28025f29f636268bc3dc79 Mon Sep 17 00:00:00 2001 From: QSD_e Date: Wed, 5 May 2021 20:37:14 +0200 Subject: [PATCH 2/8] Parse decrypted blocks --- .../download.imageset/Contents.json | 21 ++++++ .../download.imageset/Light-M.png | Bin 0 -> 430 bytes .../paperclip.imageset/Contents.json | 12 ++-- .../Msg/MessageViewController.swift | 21 +++--- FlowCrypt/Core/CoreTypes.swift | 2 +- FlowCryptUI/Nodes/AttachmentNode.swift | 61 ++++++++++++------ 6 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 FlowCrypt/Assets.xcassets/download.imageset/Contents.json create mode 100644 FlowCrypt/Assets.xcassets/download.imageset/Light-M.png diff --git a/FlowCrypt/Assets.xcassets/download.imageset/Contents.json b/FlowCrypt/Assets.xcassets/download.imageset/Contents.json new file mode 100644 index 000000000..f612e0ad5 --- /dev/null +++ b/FlowCrypt/Assets.xcassets/download.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Light-M.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FlowCrypt/Assets.xcassets/download.imageset/Light-M.png b/FlowCrypt/Assets.xcassets/download.imageset/Light-M.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3c3a5ea52ca43a9d1d4405ea649cdb1be6e15c GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^YCvqv!3HFMt5vlDDb50q$YKTtZeb8+WSBKa0w~B> z9OUlAuP=yz|Q)SsE{6O!=M)5C+o(xt$fPt7yBk=a!p=lfP+RUw~f7hpB~=n z)Y@}c&H3v?p5J?CNj*J0*Y>`;>(7E%W6La#JhLgM6`Xd<@opEvpBQ*VR2Ji5T(W$<+Mb6Mw<&;$UG ChqulE literal 0 HcmV?d00001 diff --git a/FlowCrypt/Assets.xcassets/paperclip.imageset/Contents.json b/FlowCrypt/Assets.xcassets/paperclip.imageset/Contents.json index 5f5e212a9..f977fb7df 100644 --- a/FlowCrypt/Assets.xcassets/paperclip.imageset/Contents.json +++ b/FlowCrypt/Assets.xcassets/paperclip.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { - "idiom" : "universal", "filename" : "paperclip.png", + "idiom" : "universal", "scale" : "1x" }, { - "idiom" : "universal", "filename" : "paperclip@2x.png", + "idiom" : "universal", "scale" : "2x" }, { - "idiom" : "universal", "filename" : "paperclip@3x.png", + "idiom" : "universal", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index 34b33813a..c5882c475 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -53,6 +53,7 @@ final class MessageViewController: TableNodeViewController { private let messageProvider: MessageProvider private let messageOperationsProvider: MessageOperationsProvider private var message: NSAttributedString + private var attachments: [Attachment] private let trashFolderProvider: TrashFolderProviderType init( @@ -73,6 +74,7 @@ final class MessageViewController: TableNodeViewController { self.core = core self.trashFolderProvider = trashFolderProvider self.onCompletion = completion + self.attachments = [] self.message = decorator.attributed( text: "loading_title".localized + "...", color: .lightGray @@ -143,7 +145,7 @@ extension MessageViewController { self?.message = try await(self!.fetchMessage()) }.then(on: .main) { [weak self] in self?.hideSpinner() - self?.node.reloadRows(at: [Parts.text.indexPath], with: .fade) + self?.node.reloadRows(at: [Parts.text.indexPath, Parts.attachment.indexPath], with: .fade) self?.asyncMarkAsReadIfNotAlreadyMarked() }.catch(on: .main) { [weak self] error in self?.hideSpinner() @@ -170,10 +172,10 @@ extension MessageViewController { isEmail: true ) let decryptErrBlocks = decrypted.blocks.filter { $0.decryptErr != nil } + let decryptAttBlocks = decrypted.blocks.filter { $0.type == .plainAtt || $0.type == .encryptedAtt || $0.type == .decryptedAtt } - decrypted.blocks.forEach { block in - // - } + let attachments = decryptAttBlocks.map { _ in Attachment(name: "Attachment", size: "10 MB") } + self.attachments = attachments let message: NSAttributedString if let decryptErrBlock = decryptErrBlocks.first { @@ -246,6 +248,10 @@ extension MessageViewController { showToast("Marking as unread will be implemented soon") } + @objc private func handleAttachmentTap() { + showToast("Downloading attachments is not implemented yet") + } + @objc private func handleTrashTap() { showSpinner() @@ -370,9 +376,6 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { date: input?.objMessage.date ) - let input = AttachmentsNode.Input.init(name: "Attachment 1", size: "100 MB") - let input2 = AttachmentsNode.Input.init(name: "Attachment 2", size: "16 MB") - return { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { @@ -385,7 +388,9 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { case .text: return MessageTextSubjectNode(self.message) case .attachment: - return AttachmentsNode.init(input: [input, input2]) + return AttachmentsNode(attachments: self.attachments) { [weak self] in + self?.handleAttachmentTap() + } } } } diff --git a/FlowCrypt/Core/CoreTypes.swift b/FlowCrypt/Core/CoreTypes.swift index 90959d11f..5b3e7c7db 100644 --- a/FlowCrypt/Core/CoreTypes.swift +++ b/FlowCrypt/Core/CoreTypes.swift @@ -19,7 +19,7 @@ struct CoreRes { let text: String } - struct ParseDecryptMsg: Decodable { + struct ParseDecryptMsg { let replyType: ReplyType let text: String let blocks: [MsgBlock] diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index b5e318255..76b0dde05 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -9,7 +9,15 @@ import AsyncDisplayKit public struct Attachment { - var name, size: NSAttributedString + var name, size: String + + public init( + name: String, + size: String + ) { + self.name = name + self.size = size + } } public final class AttachmentsNode: CellNode { @@ -27,21 +35,21 @@ public final class AttachmentsNode: CellNode { } private var attachmentNodes: [AttachmentNode] = [] - - public init(input: [Input]) { + private var onTap: (() -> Void)? + + public init(attachments: [Attachment], onTap: (() -> Void)?) { super.init() - input.forEach { input in - attachmentNodes.append( - AttachmentNode( - input: AttachmentNode.Input(name: input.name, size: input.size) - ) - ) - } + self.onTap = onTap + attachmentNodes = attachments.map { AttachmentNode(input: AttachmentNode.Input(name: $0.name, size: $0.size), + onTap: { + self.onTap?() + }) + } } public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { return ASInsetLayoutSpec( - insets: UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0), + insets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), child: ASStackLayoutSpec( direction: .vertical, spacing: 8, @@ -62,20 +70,29 @@ public final class AttachmentNode: CellNode { private let buttonNode = ASButtonNode() private let separatorNode = ASDisplayNode() - public init(input: Input) { + private var onTap: (() -> Void)? + + public init(input: Input, onTap: (() -> Void)?) { super.init() - self.borderWidth = 0.5 - self.borderColor = UIColor(named: "red")?.cgColor + self.onTap = onTap + + self.borderWidth = 1.0 + self.cornerRadius = 8.0 + self.borderColor = UIColor.lightGray.cgColor + + imageNode.tintColor = .gray + buttonNode.tintColor = .gray imageNode.image = UIImage(named: "paperclip") - buttonNode.setImage(UIImage(named: "paperclip"), for: .normal) - titleNode.attributedText = NSAttributedString.text(from: input.name, style: .medium(16)) - subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12)) + buttonNode.setImage(UIImage(named: "download"), for: .normal) + buttonNode.addTarget(self, action: #selector(tapHandle), forControlEvents: .touchUpInside) + titleNode.attributedText = NSAttributedString.text(from: input.name, style: .regular(18), color: .gray, alignment: .left) + subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12), color: .gray, alignment: .left) } public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { let verticalStack = ASStackLayoutSpec.vertical() - verticalStack.spacing = 6 + verticalStack.spacing = 3 verticalStack.style.flexShrink = 1.0 verticalStack.style.flexGrow = 1.0 separatorNode.style.flexGrow = 1.0 @@ -85,15 +102,19 @@ public final class AttachmentNode: CellNode { let finalSpec = ASStackLayoutSpec( direction: .horizontal, - spacing: 8, + spacing: 10, justifyContent: .start, alignItems: .center, children: [imageNode, verticalStack, separatorNode, buttonNode] ) return ASInsetLayoutSpec( - insets: UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16), + insets: UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20), child: finalSpec ) } + + @objc private func tapHandle() { + onTap?() + } } From ae373d550552e16c6c505e2a8f4d6def9e0835bb Mon Sep 17 00:00:00 2001 From: QSD_e Date: Thu, 6 May 2021 10:52:15 +0200 Subject: [PATCH 3/8] issue #193 Update color for AttachmentNode icons --- FlowCryptUI/Nodes/AttachmentNode.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index 76b0dde05..c02c521c8 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -36,7 +36,7 @@ public final class AttachmentsNode: CellNode { private var attachmentNodes: [AttachmentNode] = [] private var onTap: (() -> Void)? - + public init(attachments: [Attachment], onTap: (() -> Void)?) { super.init() self.onTap = onTap @@ -83,8 +83,8 @@ public final class AttachmentNode: CellNode { imageNode.tintColor = .gray buttonNode.tintColor = .gray - imageNode.image = UIImage(named: "paperclip") - buttonNode.setImage(UIImage(named: "download"), for: .normal) + imageNode.image = UIImage(named: "paperclip")?.tinted(.gray) + buttonNode.setImage(UIImage(named: "download")?.tinted(.gray), for: .normal) buttonNode.addTarget(self, action: #selector(tapHandle), forControlEvents: .touchUpInside) titleNode.attributedText = NSAttributedString.text(from: input.name, style: .regular(18), color: .gray, alignment: .left) subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12), color: .gray, alignment: .left) From 30b3df3be1f39ffd08c04ca0779e2ae7b0dce14e Mon Sep 17 00:00:00 2001 From: QSD_e Date: Thu, 6 May 2021 14:09:55 +0200 Subject: [PATCH 4/8] issue #193 parse attachment name and file size --- FlowCrypt/Controllers/Msg/MessageViewController.swift | 2 +- FlowCrypt/Core/CoreTypes.swift | 10 ++++++++-- FlowCryptUI/Nodes/AttachmentNode.swift | 9 +++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index c5882c475..0272ccfd7 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -174,7 +174,7 @@ extension MessageViewController { let decryptErrBlocks = decrypted.blocks.filter { $0.decryptErr != nil } let decryptAttBlocks = decrypted.blocks.filter { $0.type == .plainAtt || $0.type == .encryptedAtt || $0.type == .decryptedAtt } - let attachments = decryptAttBlocks.map { _ in Attachment(name: "Attachment", size: "10 MB") } + let attachments = decryptAttBlocks.map { Attachment(name: $0.attMeta?.name ?? "Attachment", size: $0.attMeta?.length ?? 0) } self.attachments = attachments let message: NSAttributedString diff --git a/FlowCrypt/Core/CoreTypes.swift b/FlowCrypt/Core/CoreTypes.swift index 5b3e7c7db..49a0ccd22 100644 --- a/FlowCrypt/Core/CoreTypes.swift +++ b/FlowCrypt/Core/CoreTypes.swift @@ -124,15 +124,15 @@ struct SendableMsg { struct MsgBlock: Decodable { static func blockParseErr(with content: String) -> MsgBlock { - MsgBlock(type: .blockParseErr, content: content, decryptErr: nil, keyDetails: nil) + MsgBlock(type: .blockParseErr, content: content, decryptErr: nil, keyDetails: nil, attMeta: nil) } let type: BlockType let content: String let decryptErr: DecryptErr? // always present in decryptErr BlockType let keyDetails: KeyDetails? // always present in publicKey BlockType + let attMeta: AttMeta? // always present in plainAtt, encryptedAtt, decryptedAtt, encryptedAttLink // let verifyRes: VerifyRes?, - // let attMeta: AttMeta?; // always present in plainAtt, encryptedAtt, decryptedAtt, encryptedAttLink // let signature: String? // possibly not neded in Swift @@ -166,6 +166,12 @@ struct MsgBlock: Decodable { } } + struct AttMeta: Decodable { + let name: String + let data: Data + let length: Int + } + enum BlockType: String, Decodable { case plainHtml // all content blocks, regardless if encrypted or not, formatted as a plainHtml (todo - rename this one day to formattedHtml) case publicKey diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index c02c521c8..163354add 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -13,10 +13,10 @@ public struct Attachment { public init( name: String, - size: String + size: Int ) { self.name = name - self.size = size + self.size = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .file) } } @@ -68,7 +68,6 @@ public final class AttachmentNode: CellNode { private let subtitleNode = ASTextNode2() private let imageNode = ASImageNode() private let buttonNode = ASButtonNode() - private let separatorNode = ASDisplayNode() private var onTap: (() -> Void)? @@ -95,8 +94,6 @@ public final class AttachmentNode: CellNode { verticalStack.spacing = 3 verticalStack.style.flexShrink = 1.0 verticalStack.style.flexGrow = 1.0 - separatorNode.style.flexGrow = 1.0 - separatorNode.style.preferredSize.height = 1.0 verticalStack.children = [titleNode, subtitleNode] @@ -105,7 +102,7 @@ public final class AttachmentNode: CellNode { spacing: 10, justifyContent: .start, alignItems: .center, - children: [imageNode, verticalStack, separatorNode, buttonNode] + children: [imageNode, verticalStack, buttonNode] ) return ASInsetLayoutSpec( From 5cbff47260e73f7a984a2088fe4f12604345e344 Mon Sep 17 00:00:00 2001 From: QSD_e Date: Tue, 11 May 2021 16:43:20 +0200 Subject: [PATCH 5/8] issue #193 minor code refactoring --- FlowCrypt.xcodeproj/project.pbxproj | 4 +-- .../Msg/MessageViewController.swift | 9 ++++--- FlowCrypt/Core/CoreTypes.swift | 6 +++++ FlowCryptUI/Nodes/AttachmentNode.swift | 25 ++++++++++++++----- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index a69e4411d..3f486e587 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -26,7 +26,7 @@ 32DCAF683D87EA6221F71335 /* SequenceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA058652FD4616FB04FB6 /* SequenceExtensions.swift */; }; 32DCAF95A6A329C3136B1C8E /* Imap+msg.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA55C094E9745AA1FD210 /* Imap+msg.swift */; }; 32DCAF9DA9EC47798DF8BB73 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA9701B2D5052225A0414 /* SignInViewController.swift */; }; - 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; + 5084BEA6264ABB3100755E52 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 5A39F42D239EC321001F4607 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42C239EC321001F4607 /* SettingsViewController.swift */; }; 5A39F430239EC396001F4607 /* SettingsViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42F239EC396001F4607 /* SettingsViewDecorator.swift */; }; 5A39F437239ECC23001F4607 /* KeySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F436239ECC23001F4607 /* KeySettingsViewController.swift */; }; @@ -2330,6 +2330,7 @@ D2CDC3D22402D4DA002B045F /* UIViewControllerExtensions.swift in Sources */, D297990D2444A76D004A3E31 /* UserObject+Empty.swift in Sources */, D2D27B79248A8694007346FA /* BigIntExtension.swift in Sources */, + 5084BEA6264ABB3100755E52 /* AttachmentNode.swift in Sources */, 9F003D6D25EA8F3200EB38C0 /* UserAccountService.swift in Sources */, D29AFFF92409767F00C1387D /* GoogleContactsResponse.swift in Sources */, 9F003D6125E1B4ED00EB38C0 /* TrashFolderProvider.swift in Sources */, @@ -2407,7 +2408,6 @@ D28655912423B4580066F52E /* HeaderNode.swift in Sources */, D271774C242558DA00BDA9A9 /* MessageTextSubjectNode.swift in Sources */, D27177502425659F00BDA9A9 /* SettingsCellNode.swift in Sources */, - 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */, D2CDC3D824047066002B045F /* RecipientEmailNode.swift in Sources */, D2717753242568A600BDA9A9 /* NavigationBarItemsView.swift in Sources */, D211CE7023FC35AC00D1CE38 /* TableNode.swift in Sources */, diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index 0272ccfd7..b5365eb88 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -53,7 +53,7 @@ final class MessageViewController: TableNodeViewController { private let messageProvider: MessageProvider private let messageOperationsProvider: MessageOperationsProvider private var message: NSAttributedString - private var attachments: [Attachment] + private var attachments: [Attachment] = [] private let trashFolderProvider: TrashFolderProviderType init( @@ -74,7 +74,6 @@ final class MessageViewController: TableNodeViewController { self.core = core self.trashFolderProvider = trashFolderProvider self.onCompletion = completion - self.attachments = [] self.message = decorator.attributed( text: "loading_title".localized + "...", color: .lightGray @@ -172,9 +171,11 @@ extension MessageViewController { isEmail: true ) let decryptErrBlocks = decrypted.blocks.filter { $0.decryptErr != nil } - let decryptAttBlocks = decrypted.blocks.filter { $0.type == .plainAtt || $0.type == .encryptedAtt || $0.type == .decryptedAtt } - let attachments = decryptAttBlocks.map { Attachment(name: $0.attMeta?.name ?? "Attachment", size: $0.attMeta?.length ?? 0) } + let attachments = decrypted.blocks + .filter { $0.isAttachmentBlock } + .map { Attachment(block: $0) } + self.attachments = attachments let message: NSAttributedString diff --git a/FlowCrypt/Core/CoreTypes.swift b/FlowCrypt/Core/CoreTypes.swift index 49a0ccd22..07afec6a7 100644 --- a/FlowCrypt/Core/CoreTypes.swift +++ b/FlowCrypt/Core/CoreTypes.swift @@ -186,3 +186,9 @@ struct MsgBlock: Decodable { // case cryptupVerification; // not sure if Swift code will ever encounter this } } + +extension MsgBlock { + var isAttachmentBlock: Bool { + type == .plainAtt || type == .encryptedAtt || type == .decryptedAtt + } +} diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index 163354add..9bc57cca2 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -7,6 +7,8 @@ // import AsyncDisplayKit +import FlowCryptUI +import Promises public struct Attachment { var name, size: String @@ -16,10 +18,20 @@ public struct Attachment { size: Int ) { self.name = name - self.size = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .file) + self.size = ByteCountFormatter.string(fromByteCount: Int64(size), + countStyle: .file) } } +extension Attachment { + init(block: MsgBlock) { + self.name = block.attMeta?.name ?? "/" + self.size = ByteCountFormatter.string(fromByteCount: Int64(block.attMeta?.length ?? 0), + countStyle: .file) + } +} + + public final class AttachmentsNode: CellNode { public struct Input { let name: String @@ -40,11 +52,12 @@ public final class AttachmentsNode: CellNode { public init(attachments: [Attachment], onTap: (() -> Void)?) { super.init() self.onTap = onTap - attachmentNodes = attachments.map { AttachmentNode(input: AttachmentNode.Input(name: $0.name, size: $0.size), - onTap: { - self.onTap?() - }) - } + attachmentNodes = attachments.map { + AttachmentNode( + input: AttachmentNode.Input(name: $0.name, size: $0.size), + onTap: { self.onTap?() } + ) + } } public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { From 206a049bcd121079f34edbcf130dcb72f9fa8ef5 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Wed, 12 May 2021 19:49:43 +0300 Subject: [PATCH 6/8] work on attachments --- FlowCrypt.xcodeproj/project.pbxproj | 65 +++-- FlowCrypt/{ => App}/AppReset.swift | 0 FlowCrypt/App/Logger.swift | 242 ++++++++++++++++++ FlowCrypt/{ => App}/main.swift | 0 FlowCrypt/AppDelegate.swift | 2 +- .../SegmentedViewController.swift | 1 + .../View Controllers/WebViewController.swift | 1 + .../Compose/ComposeViewController.swift | 13 +- .../EnterPassPhraseViewController.swift | 1 + .../Import Key/ImportKeyViewController.swift | 1 + .../InboxViewControllerContainer.swift | 3 +- ...nboxViewControllerContainerDecorator.swift | 2 +- .../Inbox/InboxViewController.swift | 2 +- .../Msg/MessageViewController.swift | 167 ++++++------ .../Msg/MessageViewDecorator.swift | 24 +- .../Search/SearchViewController.swift | 24 +- .../BackupOptionsViewController.swift | 3 +- .../BackupOptionsViewDecorator.swift | 4 +- .../Backups Scene/BackupViewController.swift | 3 +- .../BackupSelectKeyDecorator.swift | 6 +- .../BackupSelectKeyViewController.swift | 5 +- .../ContactDetailDecorator.swift | 2 +- .../ContactDetailViewController.swift | 1 + .../Contacts List/ContactsListDecorator.swift | 2 +- .../ContactsListViewController.swift | 3 +- .../KeyLegal/LegalViewController.swift | 1 + .../KeyDetailInfoViewController.swift | 1 + .../Key Details/KeyDetailViewController.swift | 1 + .../Key List/KeySettingsViewController.swift | 1 + .../Key List/KeySettingsViewDecorator.swift | 2 +- .../PublicKeyDetailViewController.swift | 3 +- .../SettingsViewController.swift | 1 + .../Setup/SetupViewController.swift | 48 ++-- .../SideMenu/Menu/MyMenuViewController.swift | 3 +- .../SideMenu/Menu/MyMenuViewDecorator.swift | 2 +- .../EmailProviderViewController.swift | 6 +- .../EmailProviderViewDecorator.swift | 2 +- .../SignIn/SignInViewController.swift | 20 +- .../SignIn/SignInViewDecorator.swift | 2 +- FlowCrypt/Core/Core.swift | 13 +- FlowCrypt/Core/CoreHost.swift | 11 +- FlowCrypt/Core/CoreTypes.swift | 6 - FlowCrypt/Core/Models/CoreTypesTest.swift | 3 +- FlowCrypt/Core/Models/KeyDetails.swift | 2 +- .../UIViewControllerExtensions.swift | 7 +- .../Extensions/URLSessionExtension.swift | 11 +- .../DataManager/DataService.swift | 8 +- .../Encrypted Storage/EncryptedStorage.swift | 172 ++++--------- .../Encrypted Storage/KeyChainService.swift | 10 +- .../Local Storage/LocalStorage.swift | 1 - .../DataManager/UserAccountService.swift | 16 +- .../Error Handling/ErrorHandler.swift | 2 +- .../Backup Provider/Gmail+Backup.swift | 25 +- .../Backup Provider/Imap+Backup.swift | 8 +- .../CloudContactsProvider.swift | 2 +- .../Mail Provider/Gmail/GmailService.swift | 4 +- .../Mail Provider/Imap/Imap+Other.swift | 5 +- .../Mail Provider/Imap/Imap+msg.swift | 1 - .../Mail Provider/Imap/Imap+retry.swift | 37 +-- .../Mail Provider/Imap/Imap+session.swift | 25 +- .../Mail Provider/Imap/Imap.swift | 2 + .../Mail Sessions Providers/SMTPSession.swift | 3 +- .../SessionCredentialsProvider.swift | 4 +- .../Mail Provider/MailProvider.swift | 2 - .../Message Gateway/GmailService+send.swift | 6 +- .../Message Provider/Gmail+Message.swift | 8 +- .../Message Provider/MessageService.swift | 119 +++++++++ .../Gmail+MessageOperations.swift | 12 +- .../Imap+MessageOperations.swift | 14 +- .../Gmail+MessagesList.swift | 18 +- .../Imap+MessagesList.swift | 4 +- .../MessagesList Provider/Model/Message.swift | 2 +- .../Model/MessageLabel.swift | 2 +- .../SearchMessage Provider/Gmail+Search.swift | 8 +- .../SearchMessage Provider/Imap+Search.swift | 4 +- .../Migration/DBMigrationService.swift | 64 +---- .../Functionality/Services/AppStartup.swift | 7 +- .../Functionality/Services/AttesterApi.swift | 8 +- .../Backup Services/BackupService.swift | 12 +- .../Contacts Services/ContactsService.swift | 1 - .../RemoteContactsProvider.swift | 6 +- .../Folders Services/FoldersService.swift | 4 +- .../GmailService+folders.swift | 12 +- .../TrashFolderProvider.swift | 4 +- .../Functionality/Services/GlobalRouter.swift | 14 +- .../Services/GoogleUserService.swift | 25 +- .../Services/LoggerService.swift | 51 ---- FlowCrypt/Models/Realm Models/Token.swift | 21 -- .../Models/Realm Models/UserObject.swift | 4 + .../Extensions/CollectionExtensions.swift | 2 +- FlowCryptCommon/Extensions/Either.swift | 2 +- .../Extensions/IntExtensions.swift | 12 + .../Extensions/StringExtensions.swift | 2 +- .../Extensions/UIColorExtensions.swift | 10 +- FlowCryptCommon/Trace.swift | 37 +++ FlowCryptTests/ExtensionTests.swift | 4 +- FlowCryptTests/FlowCryptCoreTests.swift | 1 - FlowCryptUI/Cell Nodes/CheckBoxTextNode.swift | 4 +- .../Cell Nodes/ContactDetailNode.swift | 3 +- FlowCryptUI/Cell Nodes/InfoCellNode.swift | 2 +- .../Cell Nodes/RecipientEmailNode.swift | 4 +- FlowCryptUI/Cell Nodes/SwitchCellNode.swift | 3 +- FlowCryptUI/Cell Nodes/TextCellNode.swift | 10 +- FlowCryptUI/Nodes/AttachmentNode.swift | 78 +----- FlowCryptUI/Nodes/TableNode.swift | 1 - FlowCryptUI/Nodes/TableViewController.swift | 1 - FlowCryptUI/Nodes/TextFieldNode.swift | 2 - .../Views/NavigationBarItemsView.swift | 1 + FlowCryptUIApplication/ViewController.swift | 6 +- FlowCryptUITests/SignInGoogleTest.swift | 3 - FlowCryptUITests/SignInTest.swift | 4 +- Podfile | 14 +- Podfile.lock | 28 +- Scripts/format.sh | 21 -- fastlane/SnapshotHelper.swift | 8 +- 115 files changed, 936 insertions(+), 807 deletions(-) rename FlowCrypt/{ => App}/AppReset.swift (100%) create mode 100644 FlowCrypt/App/Logger.swift rename FlowCrypt/{ => App}/main.swift (100%) create mode 100644 FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift delete mode 100644 FlowCrypt/Functionality/Services/LoggerService.swift delete mode 100644 FlowCrypt/Models/Realm Models/Token.swift create mode 100644 FlowCryptCommon/Trace.swift diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 3f486e587..5023c5839 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 32DCAF683D87EA6221F71335 /* SequenceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA058652FD4616FB04FB6 /* SequenceExtensions.swift */; }; 32DCAF95A6A329C3136B1C8E /* Imap+msg.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA55C094E9745AA1FD210 /* Imap+msg.swift */; }; 32DCAF9DA9EC47798DF8BB73 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA9701B2D5052225A0414 /* SignInViewController.swift */; }; + 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 5084BEA6264ABB3100755E52 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 5A39F42D239EC321001F4607 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42C239EC321001F4607 /* SettingsViewController.swift */; }; 5A39F430239EC396001F4607 /* SettingsViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42F239EC396001F4607 /* SettingsViewDecorator.swift */; }; @@ -53,11 +54,11 @@ 9F0C3C102316DD5B00299985 /* GoogleUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */; }; 9F0C3C122316DDA500299985 /* DataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C112316DDA500299985 /* DataService.swift */; }; 9F0C3C142316E69300299985 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C132316E69300299985 /* User.swift */; }; - 9F0C3C18231819B200299985 /* LoggerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C17231819B200299985 /* LoggerService.swift */; }; 9F0C3C1A231819C500299985 /* MessageKindProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C19231819C500299985 /* MessageKindProviderType.swift */; }; 9F0C3C2623194E0A00299985 /* FolderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C2523194E0A00299985 /* FolderViewModel.swift */; }; 9F17976D2368EEBD002BF770 /* SetupViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F17976C2368EEBD002BF770 /* SetupViewDecorator.swift */; }; 9F1B4A342624E49300420472 /* KeyAlgoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E26F6924F25AB800612AF1 /* KeyAlgoObject.swift */; }; + 9F1D5769263B540100477938 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8220D426336626004B2009 /* Logger.swift */; }; 9F228BA623C673AD005D2CB6 /* Springboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F228BA523C673AD005D2CB6 /* Springboard.swift */; }; 9F228BA923C67587005D2CB6 /* UserCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F228BA823C67587005D2CB6 /* UserCredentials.swift */; }; 9F228BAA23C67729005D2CB6 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCAEFF16F5D91A35791730 /* DataExtensions.swift */; }; @@ -78,6 +79,7 @@ 9F41FA28253B75F4003B970D /* BackupSelectKeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F41FA27253B75F4003B970D /* BackupSelectKeyViewController.swift */; }; 9F41FA2F253B7624003B970D /* BackupSelectKeyDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F41FA2E253B7624003B970D /* BackupSelectKeyDecorator.swift */; }; 9F4300CC2571045B00791CFB /* InboxViewControllerContainerDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4300CB2571045B00791CFB /* InboxViewControllerContainerDecorator.swift */; }; + 9F44971626430710003A9FE9 /* Trace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F72E866263ECE2A0039CF81 /* Trace.swift */; }; 9F53CB7B2555E1E300C0157A /* GmailService+folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F53CB7A2555E1E300C0157A /* GmailService+folders.swift */; }; 9F53CB872555E7F300C0157A /* Imap+Other.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F53CB862555E7F300C0157A /* Imap+Other.swift */; }; 9F56BD2C23438A8500A7371A /* Imap+messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56BD2B23438A8500A7371A /* Imap+messages.swift */; }; @@ -85,8 +87,6 @@ 9F589F0D238C7A9B007FD759 /* LocalStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F589F0C238C7A9B007FD759 /* LocalStorage.swift */; }; 9F589F11238C7DDC007FD759 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C132316E69300299985 /* User.swift */; }; 9F589F15238C8249007FD759 /* KeyChainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F589F14238C8249007FD759 /* KeyChainService.swift */; }; - 9F589F1F238C8A90007FD759 /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F589F1E238C8A90007FD759 /* Token.swift */; }; - 9F589F20238C8A90007FD759 /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F589F1E238C8A90007FD759 /* Token.swift */; }; 9F5C2A77257D705100DE9B4B /* MessageLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5C2A76257D705100DE9B4B /* MessageLabel.swift */; }; 9F5C2A7E257E64D500DE9B4B /* MessageOperationsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5C2A7D257E64D500DE9B4B /* MessageOperationsProvider.swift */; }; 9F5C2A8B257E6C4900DE9B4B /* ImapError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5C2A8A257E6C4900DE9B4B /* ImapError.swift */; }; @@ -99,6 +99,7 @@ 9F7DE8C5232029BD00F10B3E /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA0C3D34A69851A238E87 /* Core.swift */; }; 9F7DE8C6232029D000F10B3E /* CoreTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCAC732B988D9704658812 /* CoreTypes.swift */; }; 9F7DE8C723202A0200F10B3E /* CoreHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCACB22E895C2500A99350 /* CoreHost.swift */; }; + 9F8220D526336626004B2009 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8220D426336626004B2009 /* Logger.swift */; }; 9F82779823737E0900E19C07 /* MessageViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F82779723737E0900E19C07 /* MessageViewDecorator.swift */; }; 9F82D352256D74FA0069A702 /* InboxViewControllerContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F82D351256D74FA0069A702 /* InboxViewControllerContainer.swift */; }; 9F92EE72236F165E009BE0D7 /* EncryptedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F92EE71236F165E009BE0D7 /* EncryptedStorage.swift */; }; @@ -110,6 +111,7 @@ 9F9AAFFD2383E216000A00F1 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9AAFFC2383E216000A00F1 /* Document.swift */; }; 9F9ABC8723AC1EAA00D560E3 /* MessageContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9ABC8623AC1EAA00D560E3 /* MessageContext.swift */; }; 9FA19890253C841F008C9CF2 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA1988F253C841F008C9CF2 /* TableViewController.swift */; }; + 9FA9C83C264C2D75005A9670 /* MessageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA9C83B264C2D75005A9670 /* MessageService.swift */; }; 9FB22CD625715CA10026EE64 /* BackupServiceErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB22CD525715CA10026EE64 /* BackupServiceErrorHandler.swift */; }; 9FB22CDD25715CF50026EE64 /* GmailServiceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB22CDC25715CF50026EE64 /* GmailServiceError.swift */; }; 9FB22CE425715D3E0026EE64 /* GmailServiceErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB22CE325715D3E0026EE64 /* GmailServiceErrorHandler.swift */; }; @@ -400,7 +402,6 @@ 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleUserService.swift; sourceTree = ""; }; 9F0C3C112316DDA500299985 /* DataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataService.swift; sourceTree = ""; }; 9F0C3C132316E69300299985 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; - 9F0C3C17231819B200299985 /* LoggerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerService.swift; sourceTree = ""; }; 9F0C3C19231819C500299985 /* MessageKindProviderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageKindProviderType.swift; sourceTree = ""; }; 9F0C3C2523194E0A00299985 /* FolderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderViewModel.swift; sourceTree = ""; }; 9F0C3C2723194E8500299985 /* CommonExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonExtensions.swift; sourceTree = ""; }; @@ -441,7 +442,6 @@ 9F56BD3923438D3700A7371A /* StyleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyleExtension.swift; sourceTree = ""; }; 9F589F0C238C7A9B007FD759 /* LocalStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorage.swift; sourceTree = ""; }; 9F589F14238C8249007FD759 /* KeyChainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyChainService.swift; sourceTree = ""; }; - 9F589F1E238C8A90007FD759 /* Token.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Token.swift; sourceTree = ""; }; 9F5C2A76257D705100DE9B4B /* MessageLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLabel.swift; sourceTree = ""; }; 9F5C2A7D257E64D500DE9B4B /* MessageOperationsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageOperationsProvider.swift; sourceTree = ""; }; 9F5C2A8A257E6C4900DE9B4B /* ImapError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImapError.swift; sourceTree = ""; }; @@ -455,6 +455,8 @@ 9F716304234FC7200031645E /* LocalizationExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationExtensions.swift; sourceTree = ""; }; 9F716309234FC73E0031645E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 9F71630B234FC7500031645E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 9F72E866263ECE2A0039CF81 /* Trace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trace.swift; sourceTree = ""; }; + 9F8220D426336626004B2009 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 9F8277952373732000E19C07 /* UIImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageExtensions.swift; sourceTree = ""; }; 9F82779723737E0900E19C07 /* MessageViewDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageViewDecorator.swift; sourceTree = ""; }; 9F82779923737E1900E19C07 /* MessageSenderNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderNode.swift; sourceTree = ""; }; @@ -474,6 +476,7 @@ 9F9AAFFC2383E216000A00F1 /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 9F9ABC8623AC1EAA00D560E3 /* MessageContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContext.swift; sourceTree = ""; }; 9FA1988F253C841F008C9CF2 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; + 9FA9C83B264C2D75005A9670 /* MessageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageService.swift; sourceTree = ""; }; 9FB22CD525715CA10026EE64 /* BackupServiceErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceErrorHandler.swift; sourceTree = ""; }; 9FB22CDC25715CF50026EE64 /* GmailServiceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmailServiceError.swift; sourceTree = ""; }; 9FB22CE325715D3E0026EE64 /* GmailServiceErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmailServiceErrorHandler.swift; sourceTree = ""; }; @@ -836,7 +839,6 @@ children = ( A3B7C31823F576BA0022D628 /* AppStartup.swift */, 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */, - 9F0C3C17231819B200299985 /* LoggerService.swift */, 9F31AB9F232C071700CF87EA /* GlobalRouter.swift */, 32DCAC088C8BFFFAF08853AC /* AttesterApi.swift */, 32DCAC9C0512037018F434A1 /* BackendApi.swift */, @@ -857,7 +859,6 @@ D27B911C24EFE806002DF0A1 /* ContactObject.swift */, D2E26F6924F25AB800612AF1 /* KeyAlgoObject.swift */, D274724324FD1932006BA6EF /* FolderObject.swift */, - 9F589F1E238C8A90007FD759 /* Token.swift */, 04B4728B1ECE29D200B8266F /* KeyInfo.swift */, D2F41370243CC76E0066AFB5 /* SessionObject.swift */, ); @@ -1020,12 +1021,23 @@ path = "Backup Provider"; sourceTree = ""; }; + 9F8220D32633661C004B2009 /* App */ = { + isa = PBXGroup; + children = ( + 9FDF3653235A218E00614596 /* main.swift */, + 9FDF3655235A22DA00614596 /* AppReset.swift */, + 9F8220D426336626004B2009 /* Logger.swift */, + ); + path = App; + sourceTree = ""; + }; 9F9361AB2573CE430009912F /* Message Provider */ = { isa = PBXGroup; children = ( 9F9361A42573CE260009912F /* MessageProvider.swift */, 9F93623E2573D16F0009912F /* Gmail+Message.swift */, 9F9362182573D10E0009912F /* Imap+Message.swift */, + 9FA9C83B264C2D75005A9670 /* MessageService.swift */, ); path = "Message Provider"; sourceTree = ""; @@ -1253,8 +1265,7 @@ 32DCAEF9FEEF84D4F0D4A516 /* Extensions */, C132B9C61EC2DCC000763715 /* Functionality */, A3DAD60C22E469E400F2C4CD /* Resources */, - 9FDF3653235A218E00614596 /* main.swift */, - 9FDF3655235A22DA00614596 /* AppReset.swift */, + 9F8220D32633661C004B2009 /* App */, C132B9B31EC2DBD800763715 /* AppDelegate.swift */, C132B9DF1EC333AA00763715 /* FlowCrypt-Bridging-Header.h */, C132B9BA1EC2DBD800763715 /* Assets.xcassets */, @@ -1386,6 +1397,7 @@ 9F56BD2D23438ABA00A7371A /* TapTicFeedback.swift */, D254AA5F24092A9E0041CAE0 /* Extensions */, D29AFFFD240990BE00C1387D /* Throttler.swift */, + 9F72E866263ECE2A0039CF81 /* Trace.swift */, ); path = FlowCryptCommon; sourceTree = ""; @@ -2190,6 +2202,7 @@ A357699622EA2BC8009242C4 /* KeyInfo.swift in Sources */, 9FC411902596229D001180A8 /* AppErr.swift in Sources */, D212D35E24C1AACF00035991 /* PrvKeyInfo.swift in Sources */, + 9F1D5769263B540100477938 /* Logger.swift in Sources */, A3DAD5FE22E4574B00F2C4CD /* FlowCryptCoreTests.swift in Sources */, D2A9CA45242622F800E1D898 /* GeneralConstantsTest.swift in Sources */, A3DAD60B22E458C300F2C4CD /* DataExtensions.swift in Sources */, @@ -2209,7 +2222,6 @@ 9F7DE8C723202A0200F10B3E /* CoreHost.swift in Sources */, 9F3EF32823B15C8400FA0CEF /* ImapHelper.swift in Sources */, 9F003D9E25EA910B00EB38C0 /* LocalStorageTests.swift in Sources */, - 9F589F20238C8A90007FD759 /* Token.swift in Sources */, 32DCAF683D87EA6221F71335 /* SequenceExtensions.swift in Sources */, 9F589F11238C7DDC007FD759 /* User.swift in Sources */, D2E26F6D24F263B600612AF1 /* KeyAlgo.swift in Sources */, @@ -2268,7 +2280,6 @@ 9FBEAE5525D41BFF009E98D4 /* UserMailSessionProvider.swift in Sources */, D212D36424C1AC4800035991 /* KeyId.swift in Sources */, D27B911924EFE79F002DF0A1 /* LocalContactsProvider.swift in Sources */, - 9F589F1F238C8A90007FD759 /* Token.swift in Sources */, 9FE1B3942563F98600D6D086 /* Imap+MessagesList.swift in Sources */, 32DCA04CA0DAB79C39514782 /* CoreTypes.swift in Sources */, 9FB22CDD25715CF50026EE64 /* GmailServiceError.swift in Sources */, @@ -2277,7 +2288,6 @@ 9FF0673325520DE400FCC9E6 /* GmailService+send.swift in Sources */, D20D3C892520B67C00D4AA9A /* BackupOptionsViewController.swift in Sources */, D20D3C672520AB1000D4AA9A /* BackupViewController.swift in Sources */, - 9F0C3C18231819B200299985 /* LoggerService.swift in Sources */, D2E26F6324F1698100612AF1 /* ContactsListViewController.swift in Sources */, 9F53CB7B2555E1E300C0157A /* GmailService+folders.swift in Sources */, D2E26F6624F169B400612AF1 /* ContactsListDecorator.swift in Sources */, @@ -2297,6 +2307,7 @@ D27B911B24EFE7BE002DF0A1 /* RemoteContactsProvider.swift in Sources */, 32DCA1414EEA727B86C337D5 /* Core.swift in Sources */, 9FB22CF725715DC50026EE64 /* KeyServiceErrorHandler.swift in Sources */, + 9F8220D526336626004B2009 /* Logger.swift in Sources */, 32DCA557DA4EF81A74CEF951 /* TestData.swift in Sources */, 9F0C3C1A231819C500299985 /* MessageKindProviderType.swift in Sources */, 9FF0670825520CF800FCC9E6 /* GmailService.swift in Sources */, @@ -2326,6 +2337,7 @@ 9F0C3C102316DD5B00299985 /* GoogleUserService.swift in Sources */, D24F4C2223E2359B00C5EEE4 /* BootstrapViewController.swift in Sources */, D211CE7623FC36BC00D1CE38 /* UIColorExtension.swift in Sources */, + 9FA9C83C264C2D75005A9670 /* MessageService.swift in Sources */, D2FF6966243115EC007182F0 /* EmailProviderViewController.swift in Sources */, D2CDC3D22402D4DA002B045F /* UIViewControllerExtensions.swift in Sources */, D297990D2444A76D004A3E31 /* UserObject+Empty.swift in Sources */, @@ -2408,6 +2420,7 @@ D28655912423B4580066F52E /* HeaderNode.swift in Sources */, D271774C242558DA00BDA9A9 /* MessageTextSubjectNode.swift in Sources */, D27177502425659F00BDA9A9 /* SettingsCellNode.swift in Sources */, + 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */, D2CDC3D824047066002B045F /* RecipientEmailNode.swift in Sources */, D2717753242568A600BDA9A9 /* NavigationBarItemsView.swift in Sources */, D211CE7023FC35AC00D1CE38 /* TableNode.swift in Sources */, @@ -2433,6 +2446,7 @@ D254AA6124092AC70041CAE0 /* LocalizationExtensions.swift in Sources */, D2531F3F24000E57007E5198 /* StringExtensions.swift in Sources */, D2CDC3CE2402CDB4002B045F /* DispatchTimeExtension.swift in Sources */, + 9F44971626430710003A9FE9 /* Trace.swift in Sources */, D2CDC3CD2402CCD7002B045F /* UIImageExtensions.swift in Sources */, D29AFFF6240939AE00C1387D /* Then.swift in Sources */, D2531F3423FEEF5F007E5198 /* StyleExtension.swift in Sources */, @@ -2672,7 +2686,7 @@ "\"$(BUILT_PRODUCTS_DIR)/../../include\"/**", ); INFOPLIST_FILE = FlowCrypt/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2701,6 +2715,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2731,6 +2746,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2761,6 +2777,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2922,7 +2939,7 @@ "\"$(BUILT_PRODUCTS_DIR)/../../include\"/**", ); INFOPLIST_FILE = FlowCrypt/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2965,7 +2982,7 @@ "\"$(BUILT_PRODUCTS_DIR)/../../include\"/**", ); INFOPLIST_FILE = FlowCrypt/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2999,7 +3016,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3037,7 +3054,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3073,7 +3090,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3107,7 +3124,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptCommon/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3142,7 +3159,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptCommon/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3176,7 +3193,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptCommon/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3206,7 +3223,7 @@ DEVELOPMENT_TEAM = W57XRJ27NX; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUIApplication/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3232,7 +3249,7 @@ DEVELOPMENT_TEAM = W57XRJ27NX; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUIApplication/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3257,7 +3274,7 @@ DEVELOPMENT_TEAM = W57XRJ27NX; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = FlowCryptUIApplication/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/FlowCrypt/AppReset.swift b/FlowCrypt/App/AppReset.swift similarity index 100% rename from FlowCrypt/AppReset.swift rename to FlowCrypt/App/AppReset.swift diff --git a/FlowCrypt/App/Logger.swift b/FlowCrypt/App/Logger.swift new file mode 100644 index 000000000..bee940c46 --- /dev/null +++ b/FlowCrypt/App/Logger.swift @@ -0,0 +1,242 @@ +// +// Logger.swift +// FlowCrypt +// +// Created by Anton Kharchevskyi on 23.04.2021. +// Copyright © 2021 FlowCrypt Limited. All rights reserved. +// + +import Foundation + +// MARK: - Documentation +// +// ******* To print ALL messages ******* +// Change +// Configuration.default isAll = true +// +// +// ******* To print with log level ******* +// Change +// Configuration.default isAll = false +// Configuration.default logLevel = (.level) +// +// +// ******* Convenience Usage ******* +// +// let logger = Logger.nested(in: Self.self, with: "Flow name") +// logger.logDebug("check is user logged in") +// "⚙️[Flow name][GlobalRouter][23:53:17] check is user logged in" +// +// let logger = Logger.nested("App Start") +// logger.logDebug("some message") +// +// +// ******* OR ******* +// Logger.logDebug("some message") +// +// ******* Nested Logger ******* +// inside some class +// let logger = Logger.nested(Self.self) +// logger.logWarning("some") + +// MARK: - Implementation +struct Logger { + private struct Configuration { + // MARK: - Default logLevel + static let `default`: Configuration = .init( + isAll: true, + logLevel: .warning, + shouldShowPath: false, + shouldShowTime: false + ) + + let isAll: Bool + let logLevel: Logger.Level + /// Add fupath to message + let shouldShowPath: Bool + /// Add time to message + let shouldShowTime: Bool + } + + private enum Level: Equatable, Comparable { + case verbose + case info + case debug + case error + case warning + + var label: String { + switch self { + case .verbose: return "🏷" + case .info: return "ℹ️" + case .debug: return "⚙️" + case .error: return "❗️" + case .warning: return "🔥" + } + } + } + + static var dateFormatter = DateFormatter().then { + $0.dateFormat = "HH:mm:ss" + $0.locale = .current + $0.timeZone = .current + } + + private let config: Configuration + private let label: String? + + private init(config: Configuration = .default, label: String? = nil) { + self.config = config + self.label = label + } + + private func log( + _ level: Logger.Level, + _ message: @autoclosure () -> String, + file: String = #file, + function: String = #function, + line: UInt = #line + ) { + var shouldPrint = false + + if config.isAll { + shouldPrint = true + } else { + shouldPrint = level >= config.logLevel + } + + guard shouldPrint else { return } + + var messageToPrint = "" + + // "ℹ️" + messageToPrint.append("\(level.label)") + + // "ℹ️[App Start]" + if let label = self.label { + messageToPrint.append("\(label)") + } + + // "ℹ️[App Start][GlobalRouter-proceed-56]" + if config.shouldShowPath { + messageToPrint.append("[\(file)-\(function)-\(line)]") + } + + // "ℹ️[App Start][GlobalRouter-proceed-56][11:25:02]" + if config.shouldShowTime { + messageToPrint.append("[\(Self.dateFormatter.string(from: Date()))]") + } + + messageToPrint.append(" ") + // "ℹ️[App Start][GlobalRouter-proceed-56][11:25:02] Some message goes here" + messageToPrint.append(message()) + + debugPrint(messageToPrint) + } +} + +// MARK: - Nested +extension Logger { + + static func nested(_ label: String) -> Logger { + Logger(config: .default, label: "[\(label)]") + } + + static func nested(_ type: T.Type) -> Logger { + Logger(config: .default, label: "[\(String(describing: type))]") + } + + static func nested(in type: T.Type, with label: String) -> Logger { + var message = "[\(label)]" + message.append("[\(String(describing: type))]") + return Logger(config: .default, label: message) + } +} + +// MARK: - Nested with app label +extension Logger { + // MARK: - Log Labels + enum LogLabels: String { + /// log all events which is important for app start for a user + case userAppStart = "App Start" + + /// log all db migration events + case migration = "Migration" + } + + static func nested(in type: T.Type, with logLabel: LogLabels) -> Logger { + Self.nested(in: type, with: logLabel.rawValue) + } +} + +// MARK: - Instance +extension Logger { + /// verbose messages + func logVerbose(_ message: String) { + log(.verbose, message) + } + + /// default log level to print some information message + func logInfo(_ message: String) { + log(.info, message) + } + + /// debug log level for debugging some issues during development. Consider info log level to any other messages + func logDebug(_ message: String) { + log(.debug, message) + } + + /// log errors only + func logError(_ message: String) { + log(.error, message) + } + + /// log important warings + func logWarning(_ message: String) { + log(.warning, message) + } +} + +// MARK: - Static +extension Logger { + private static let logger = Logger(config: .default) + + /// verbose messages + static func logVerbose(_ message: String) { + logger.log(.verbose, message) + } + + /// default log level to print some information message + static func logInfo(_ message: String) { + logger.log(.info, message) + } + + /// debug log level for debugging some issues during development. Consider info log level to any other messages + static func logDebug(_ message: String) { + logger.log(.debug, message) + } + + /// log errors only + static func logError(_ message: String) { + logger.log(.error, message) + } + + /// log important warings + static func logWarning(_ message: String) { + logger.log(.warning, message) + } +} + +// MARK: - print +// By default the print() will print to the console for both release and debug builds. +/// Wrapping Swift.print() inside DEBUG flag +func print(_ object: Any) { + // Only allowing in DEBUG mode + #if DEBUG + Swift.print(object) + #endif +} + +func releasePrint(_ object: Any) { + Swift.print(object) +} diff --git a/FlowCrypt/main.swift b/FlowCrypt/App/main.swift similarity index 100% rename from FlowCrypt/main.swift rename to FlowCrypt/App/main.swift diff --git a/FlowCrypt/AppDelegate.swift b/FlowCrypt/AppDelegate.swift index 7eb97c628..8a96beb0a 100644 --- a/FlowCrypt/AppDelegate.swift +++ b/FlowCrypt/AppDelegate.swift @@ -3,8 +3,8 @@ // FlowCrypt // -import UIKit import AppAuth +import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var googleAuthSession: OIDExternalUserAgentSession? diff --git a/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift b/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift index 9040837a0..5a9f24ae5 100644 --- a/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift +++ b/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift @@ -47,6 +47,7 @@ final class SegmentedViewController: ASDKViewController { super.init(node: ASDisplayNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Common UI/View Controllers/WebViewController.swift b/FlowCrypt/Common UI/View Controllers/WebViewController.swift index 482593dd3..c33fc7891 100644 --- a/FlowCrypt/Common UI/View Controllers/WebViewController.swift +++ b/FlowCrypt/Common UI/View Controllers/WebViewController.swift @@ -19,6 +19,7 @@ final class WebViewController: UIViewController { super.init(nibName: nil, bundle: nil) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 23f268145..1474b7b3f 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -89,6 +89,7 @@ final class ComposeViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -212,7 +213,7 @@ extension ComposeViewController { showSpinner("sending_title".localized) Promise { [weak self] in - try await(self!.encryptAndSendMessage()) + try awaitPromise(self!.encryptAndSendMessage()) }.then(on: .main) { [weak self] sent in if sent { // else it must have shown error to user self?.handleSuccessfullySentMessage() @@ -258,7 +259,7 @@ extension ComposeViewController { to: recipients.map { $0.email } ) - try await(self.messageSender.sendMail(mime: encrypted.mimeEncoded)) + try awaitPromise(self.messageSender.sendMail(mime: encrypted.mimeEncoded)) return true } @@ -756,15 +757,11 @@ extension ComposeViewController { let okAction = UIAlertAction( title: "Log out", style: .default - ) { _ in - debugPrint("Log out") - } + ) { _ in } let cancelAction = UIAlertAction( title: "Cancel", style: .destructive - ) { _ in - debugPrint("Cancel") - } + ) { _ in } alert.addAction(okAction) alert.addAction(cancelAction) diff --git a/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift b/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift index c27276db6..a00edbd9d 100644 --- a/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift +++ b/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift @@ -47,6 +47,7 @@ final class EnterPassPhraseViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift b/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift index 96ad60e18..43d90c63c 100644 --- a/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift +++ b/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift @@ -41,6 +41,7 @@ final class ImportKeyViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainer.swift b/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainer.swift index 6390f0196..223d0b114 100644 --- a/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainer.swift +++ b/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainer.swift @@ -7,8 +7,8 @@ // import AsyncDisplayKit -import FlowCryptUI import FlowCryptCommon +import FlowCryptUI // MARK: - InboxViewControllerContainer // Used to fetch folders and get correct path for "inbox" folder @@ -45,6 +45,7 @@ final class InboxViewControllerContainer: TableNodeViewController { node.dataSource = self } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainerDecorator.swift b/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainerDecorator.swift index fbff051e2..479e4ff4e 100644 --- a/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainerDecorator.swift +++ b/FlowCrypt/Controllers/Inbox/Container/InboxViewControllerContainerDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit import FlowCryptUI +import UIKit struct InboxViewControllerContainerDecorator { func emptyFoldersInput(with size: CGSize) -> TextCellNode.Input { diff --git a/FlowCrypt/Controllers/Inbox/InboxViewController.swift b/FlowCrypt/Controllers/Inbox/InboxViewController.swift index 925c6a3dd..d6d56067c 100644 --- a/FlowCrypt/Controllers/Inbox/InboxViewController.swift +++ b/FlowCrypt/Controllers/Inbox/InboxViewController.swift @@ -77,6 +77,7 @@ final class InboxViewController: ASDKViewController { tableNode.leadingScreensForBatching = 1 } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -111,7 +112,6 @@ final class InboxViewController: ASDKViewController { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - guard #available(iOS 13.0, *) else { return } tableNode.reloadData() } } diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index b5365eb88..245f64ff2 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -12,10 +12,14 @@ final class MessageViewController: TableNodeViewController { var bodyMessage: Data? var path = "" } + + enum Sections: Int, CaseIterable { + case main, attributes + } enum Parts: Int, CaseIterable { - case sender, subject, text, attachment - + case sender, subject, text + var indexPath: IndexPath { IndexPath(row: rawValue, section: 0) } @@ -47,41 +51,34 @@ final class MessageViewController: TableNodeViewController { private let onCompletion: MsgViewControllerCompletion? private var input: MessageViewController.Input? - private let decorator: MessageViewDecoratorType - private var dataService: DataServiceType & KeyDataServiceType - private let core: Core - private let messageProvider: MessageProvider + private let decorator: MessageViewDecorator + private let messageService: MessageService private let messageOperationsProvider: MessageOperationsProvider - private var message: NSAttributedString - private var attachments: [Attachment] = [] private let trashFolderProvider: TrashFolderProviderType + private var fetchedMessage: FetchedMessage = .empty init( - messageProvider: MessageProvider = MailProvider.shared.messageProvider, + messageService: MessageService = MessageService(), messageOperationsProvider: MessageOperationsProvider = MailProvider.shared.messageOperationsProvider, - decorator: MessageViewDecoratorType = MessageViewDecorator(dateFormatter: DateFormatter()), + decorator: MessageViewDecorator = MessageViewDecorator(dateFormatter: DateFormatter()), storage: DataServiceType & KeyDataServiceType = DataService.shared, - core: Core = Core.shared, trashFolderProvider: TrashFolderProviderType = TrashFolderProvider(), input: MessageViewController.Input, completion: MsgViewControllerCompletion? ) { - self.messageProvider = messageProvider + self.messageService = messageService self.messageOperationsProvider = messageOperationsProvider self.input = input self.decorator = decorator - self.dataService = storage - self.core = core self.trashFolderProvider = trashFolderProvider self.onCompletion = completion - self.message = decorator.attributed( - text: "loading_title".localized + "...", - color: .lightGray - ) + + self.fetchedMessage = .empty super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -140,62 +137,24 @@ extension MessageViewController { private func fetchDecryptAndRenderMsg() { guard let input = input else { return } showSpinner("loading_title".localized, isUserInteractionEnabled: true) + Promise { [weak self] in - self?.message = try await(self!.fetchMessage()) - }.then(on: .main) { [weak self] in + guard let self = self else { return } + let promise = self.messageService.getMessage(with: input.objMessage, folder: input.path) + let message = try awaitPromise(promise) + self.fetchedMessage = message + } + .then(on: .main) { [weak self] in self?.hideSpinner() - self?.node.reloadRows(at: [Parts.text.indexPath, Parts.attachment.indexPath], with: .fade) + self?.node.reloadData() self?.asyncMarkAsReadIfNotAlreadyMarked() - }.catch(on: .main) { [weak self] error in + } + .catch(on: .main) { [weak self] error in self?.hideSpinner() self?.handleError(error, path: input.path) } } - private func fetchMessage() -> Promise { - Promise { [weak self] resolve, reject in - guard let self = self, let input = self.input else { return } - - let rawMimeData: Data = try await(self.messageProvider.fetchMsg(message: input.objMessage, folder: input.path)) - self.input?.bodyMessage = rawMimeData - - guard let keys = self.dataService.keys else { - reject(CoreError.notReady("Could not fetch keys")) - return - } - - let decrypted = try self.core.parseDecryptMsg( - encrypted: rawMimeData, - keys: keys, - msgPwd: nil, - isEmail: true - ) - let decryptErrBlocks = decrypted.blocks.filter { $0.decryptErr != nil } - - let attachments = decrypted.blocks - .filter { $0.isAttachmentBlock } - .map { Attachment(block: $0) } - - self.attachments = attachments - - let message: NSAttributedString - if let decryptErrBlock = decryptErrBlocks.first { - let rawMsg = decryptErrBlock.content - let err = decryptErrBlock.decryptErr?.error - message = self.decorator.attributed( - text: "Could not decrypt:\n\(err?.type.rawValue ?? "UNKNOWN"): \(err?.message ?? "??")\n\n\n\(rawMsg)", - color: .red - ) - } else { - message = self.decorator.attributed( - text: decrypted.text, - color: decrypted.replyType == CoreRes.ReplyType.encrypted ? .main : UIColor.mainTextColor - ) - } - resolve(message) - } - } - private func handleError(_ error: Error, path: String) { if let someError = error as NSError?, someError.code == Imap.Err.fetch.rawValue { // todo - the missing msg should be removed from the list in inbox view @@ -277,12 +236,12 @@ extension MessageViewController { Promise { [weak self] () -> Bool in guard let self = self else { throw AppErr.nilSelf } - guard try await(self.awaitUserConfirmation(title: "You're about to permanently delete a message")) else { return false } - try await(self.messageOperationsProvider.delete(message: input.objMessage, form: input.path)) + guard try awaitPromise(self.awaitUserConfirmation(title: "You're about to permanently delete a message")) else { return false } + try awaitPromise(self.messageOperationsProvider.delete(message: input.objMessage, form: input.path)) return true } .then(on: .main) { [weak self] didPerformOp in - guard didPerformOp else { self?.hideSpinner(); return } + guard didPerformOp else { self?.hideSpinner(); return } self?.handleOpSuccess(operation: .permanentlyDelete) }.catch(on: .main) { [weak self] _ in self?.handleOpErr(operation: .permanentlyDelete) @@ -333,9 +292,9 @@ extension MessageViewController { let replyInfo = ComposeViewController.Input.ReplyInfo( recipient: input.objMessage.sender, subject: input.objMessage.subject, - mime: input.bodyMessage, + mime: fetchedMessage.rawMimeData, sentDate: input.objMessage.date, - message: message.string + message: fetchedMessage.text ) navigationController?.pushViewController( @@ -362,11 +321,38 @@ extension MessageViewController: NavigationChildController { // MARK: - ASTableDelegate, ASTableDataSource extension MessageViewController: ASTableDelegate, ASTableDataSource { - func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int { - return Parts.allCases.count + func numberOfSections(in tableNode: ASTableNode) -> Int { + Sections.allCases.count } - + + func tableNode(_: ASTableNode, numberOfRowsInSection section: Int) -> Int { + guard let section = Sections(rawValue: section) else { + return 0 + } + switch section { + case .main: + return Parts.allCases.count + case .attributes: + return fetchedMessage.attachments.count + } + } + func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { + return { [weak self] in + guard let self = self, let section = Sections(rawValue: indexPath.section) else { return ASCellNode() } + + switch section { + case .main: + return self.mainSectionNode(for: indexPath.row) + case .attributes: + return self.attachmentNode(for: indexPath.row) + } + } + } + + private func mainSectionNode(for index: Int) -> ASCellNode { + guard let part = Parts(rawValue: index) else { return ASCellNode() } + let senderTitle = decorator.attributed( title: input?.objMessage.sender ?? "(unknown sender)" ) @@ -376,23 +362,24 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { let time = decorator.attributed( date: input?.objMessage.date ) - - return { [weak self] in - guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } - switch part { - case .sender: - return MessageSenderNode(senderTitle) { [weak self] in - self?.handleReplyTap() - } - case .subject: - return MessageSubjectNode(subject, time: time) - case .text: - return MessageTextSubjectNode(self.message) - case .attachment: - return AttachmentsNode(attachments: self.attachments) { [weak self] in - self?.handleAttachmentTap() - } + + switch part{ + case .sender: + return MessageSenderNode(senderTitle) { [weak self] in + self?.handleReplyTap() } + case .subject: + return MessageSubjectNode(subject, time: time) + case .text: + let messageInput = self.decorator.attributedMessage(from: self.fetchedMessage) + return MessageTextSubjectNode(messageInput) + } + } + + private func attachmentNode(for index: Int) -> ASCellNode { + let attachment = fetchedMessage.attachments[index] + return AttachmentNode(input: .init(msgAttachment: attachment)) { [weak self] in + self?.handleAttachmentTap() } } } diff --git a/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift b/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift index 961aaa4fd..5f1f18691 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift @@ -8,14 +8,7 @@ import UIKit -protocol MessageViewDecoratorType { - func attributed(title: String) -> NSAttributedString - func attributed(subject: String) -> NSAttributedString - func attributed(date: Date?) -> NSAttributedString - func attributed(text: String?, color: UIColor) -> NSAttributedString -} - -struct MessageViewDecorator: MessageViewDecoratorType { +struct MessageViewDecorator { let dateFormatter: DateFormatter func attributed(title: String) -> NSAttributedString { @@ -36,4 +29,19 @@ struct MessageViewDecorator: MessageViewDecoratorType { func attributed(text: String?, color: UIColor) -> NSAttributedString { (text ?? "").attributed(.regular(17), color: color) } + + func attributedMessage(from fetchedMessage: FetchedMessage) -> NSAttributedString { + fetchedMessage.text.attributed() + } +} + +extension AttachmentNode.Input { + init(msgAttachment: MessageAttachment) { + self.init( + name: msgAttachment.name + .attributed(.regular(18), color: .textColor, alignment: .left), + size: "\(msgAttachment.size)" + .attributed(.medium(12), color: .textColor, alignment: .left) + ) + } } diff --git a/FlowCrypt/Controllers/Search/SearchViewController.swift b/FlowCrypt/Controllers/Search/SearchViewController.swift index b46a6bf7a..ed9b745ec 100644 --- a/FlowCrypt/Controllers/Search/SearchViewController.swift +++ b/FlowCrypt/Controllers/Search/SearchViewController.swift @@ -48,6 +48,7 @@ final class SearchViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -85,10 +86,7 @@ extension SearchViewController { $0.searchBar.setImage(#imageLiteral(resourceName: "search_icn").tinted(.white), for: .search, state: .normal) $0.searchBar.setImage(#imageLiteral(resourceName: "cancel.png").tinted(.white), for: .clear, state: .normal) $0.searchBar.delegate = self - $0.searchBar.textField?.textColor = .white - if #available(iOS 12, *) { - $0.searchBar.textField?.backgroundColor = .main - } + $0.searchBar.searchTextField.textColor = .white } update(searchController: searchController) definesPresentationContext = true @@ -219,7 +217,7 @@ extension SearchViewController: UISearchControllerDelegate, UISearchBarDelegate } private func update(searchController: UISearchController) { - searchController.searchBar.textField? + searchController.searchBar.searchTextField .attributedPlaceholder = "search_placeholder" .localized .attributed( @@ -227,7 +225,7 @@ extension SearchViewController: UISearchControllerDelegate, UISearchBarDelegate color: UIColor.white.withAlphaComponent(0.7), alignment: .left ) - searchController.searchBar.textField?.textColor = .white + searchController.searchBar.searchTextField.textColor = .white } } @@ -321,17 +319,3 @@ extension SearchViewController: UISearchResultsUpdating { } } } - -// Support for iOS 12 and iOS 13 UISearchBar textField -private extension UISearchBar { - var textField: UITextField? { - if #available(iOS 13.0, *) { - return searchTextField - } else { - return subviews.first? - .subviews - .compactMap { $0 as? UITextField } - .first - } - } -} diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index 52d23235f..fb9a5a643 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import FlowCryptUI import AsyncDisplayKit +import FlowCryptUI enum BackupOption: Int, CaseIterable, Equatable { case email, download @@ -47,6 +47,7 @@ final class BackupOptionsViewController: ASDKViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewDecorator.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewDecorator.swift index 5e15328f2..262277646 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit import FlowCryptUI +import UIKit protocol BackupOptionsViewDecoratorType { var sceneTitle: String { get } @@ -62,7 +62,7 @@ struct BackupOptionsViewDecorator: BackupOptionsViewDecoratorType { ? .main : .lightGray - return CheckBoxTextNode.Input.init( + return CheckBoxTextNode.Input( title: attributedTitle, insets: .side(16), preferredSize: CGSize(width: 30, height: 30), diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift index 6860bdee2..a32368891 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import FlowCryptUI import AsyncDisplayKit +import FlowCryptUI final class BackupViewController: ASDKViewController { private enum Parts: Int, CaseIterable { @@ -50,6 +50,7 @@ final class BackupViewController: ASDKViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyDecorator.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyDecorator.swift index 4547d9776..35eb1b7cc 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyDecorator.swift @@ -6,9 +6,9 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit -import FlowCryptUI import FlowCryptCommon +import FlowCryptUI +import UIKit protocol BackupSelectKeyDecoratorType { var sceneTitle: String { get } @@ -37,7 +37,7 @@ struct BackupSelectKeyDecorator: BackupSelectKeyDecoratorType { ? .main : .lightGray - return CheckBoxTextNode.Input.init( + return CheckBoxTextNode.Input( title: title, subtitle: subtitle, insets: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16), diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index 878f85c30..b3c4fe98b 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -6,9 +6,9 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Foundation -import FlowCryptUI import AsyncDisplayKit +import FlowCryptUI +import Foundation final class BackupSelectKeyViewController: ASDKViewController { private let backupService: BackupServiceType @@ -34,6 +34,7 @@ final class BackupSelectKeyViewController: ASDKViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift index 7de7871aa..1a02af594 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Foundation import FlowCryptUI +import Foundation protocol ContactDetailDecoratorType { var title: String { get } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift index c5eb4c53c..b9a0b6f89 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift @@ -31,6 +31,7 @@ final class ContactDetailViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift index 645428be0..3be4721ac 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit import FlowCryptUI +import UIKit protocol ContactsListDecoratorType { var title: String { get } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift index 08dbf682b..5eccbf0c6 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift @@ -6,9 +6,9 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import FlowCryptUI import AsyncDisplayKit import FlowCryptCommon +import FlowCryptUI final class ContactsListViewController: TableNodeViewController { private let decorator: ContactsListDecoratorType @@ -24,6 +24,7 @@ final class ContactsListViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/KeyLegal/LegalViewController.swift b/FlowCrypt/Controllers/Settings/KeyLegal/LegalViewController.swift index e395201cd..e4c1c85e9 100644 --- a/FlowCrypt/Controllers/Settings/KeyLegal/LegalViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeyLegal/LegalViewController.swift @@ -21,6 +21,7 @@ final class LegalViewController: UIViewController { super.init(nibName: nil, bundle: nil) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift index 137478c63..db5a8fce9 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift @@ -31,6 +31,7 @@ final class KeyDetailInfoViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift index a81afe14e..6a171c61d 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift @@ -29,6 +29,7 @@ final class KeyDetailViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift index 06904fda2..6be74e2e3 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift @@ -23,6 +23,7 @@ final class KeySettingsViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewDecorator.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewDecorator.swift index a51c58d2f..e5ae328ef 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewDecorator.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // -import Foundation import FlowCryptCommon +import Foundation protocol KeySettingsViewDecoratorType { func attributedUsers(key: KeyDetails) -> NSAttributedString diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift index 36b39cab7..4a90ba330 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift @@ -17,6 +17,7 @@ final class PublicKeyDetailViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -38,7 +39,7 @@ extension PublicKeyDetailViewController: ASTableDelegate, ASTableDataSource { func tableNode(_: ASTableNode, nodeBlockForRowAt _: IndexPath) -> ASCellNodeBlock { return { [weak self] in - return SetupTitleNode( + SetupTitleNode( SetupTitleNode.Input( title: (self?.text ?? "").attributed(.regular(16)), insets: .side(16), diff --git a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift index 7dea2f913..6b31bbeec 100644 --- a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift @@ -39,6 +39,7 @@ final class SettingsViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCrypt/Controllers/Setup/SetupViewController.swift b/FlowCrypt/Controllers/Setup/SetupViewController.swift index 1f65a1a16..9aa5dda35 100644 --- a/FlowCrypt/Controllers/Setup/SetupViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupViewController.swift @@ -43,6 +43,13 @@ final class SetupViewController: TableNodeViewController { case createKey /// error state case error(SetupError) + + var isSearchingBackups: Bool { + guard case .searchingBackups = self else { + return false + } + return true + } } private var state: State = .idle { @@ -73,6 +80,7 @@ final class SetupViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -144,9 +152,11 @@ extension SetupViewController { case .idle: node.reloadData() case .searchingBackups: + showSpinner() searchBackups() case let .fetchedEncrypted(details): handleBackupsFetchResult(with: details) + hideSpinner() case let .error(error): hideSpinner() handleError(with: error) @@ -157,13 +167,16 @@ extension SetupViewController { } private func searchBackups() { - showSpinner() + Logger.logInfo("[Setup] searching for backups in inbox") backupService.fetchBackups(for: user) .then(on: .main) { [weak self] keys in + Logger.logInfo("[Setup] done searching for backups in inbox") guard keys.isNotEmpty else { + Logger.logInfo("[Setup] no key backups found in inbox") self?.state = .error(.noBackups) return } + Logger.logInfo("[Setup] \(keys.count) key backups found in inbox") self?.state = .fetchedEncrypted(keys) } .catch(on: .main) { [weak self] error in @@ -171,21 +184,7 @@ extension SetupViewController { } } - private func fetchEnctyptedKeys(with backupData: Data) { - showSpinner() - - do { - let parsed = try core.parseKeys(armoredOrBinary: backupData) - let keys = parsed.keyDetails.filter { $0.private != nil } - state = .fetchedEncrypted(keys) - } catch { - state = .error(.parseKey(error)) - } - } - private func handleBackupsFetchResult(with keys: [KeyDetails]) { - hideSpinner() - guard keys.isNotEmpty else { state = .error(.emptyFetchedKeys) return @@ -216,13 +215,14 @@ extension SetupViewController { extension SetupViewController { private func handleError(with error: SetupError) { + Logger.logWarning("[Setup] handling error during setup: \(error)") switch error { case .emptyFetchedKeys: let user = DataService.shared.email ?? "unknown_title".localized let msg = "setup_no_backups".localized + user showSearchBackupError(with: msg) case .noBackups: - showSearchBackupError(with: "setup_action_failed".localized) + showSearchBackupError(with: "setup_no_backups".localized) case let .parseKey(error): showErrorAlert(with: "setup_action_failed".localized, error: error) } @@ -313,9 +313,9 @@ extension SetupViewController { Promise { [weak self] in guard let self = self else { return } let userId = try self.getUserId() - try await(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) + try awaitPromise(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) let encryptedPrv = try self.core.generateKey(passphrase: passPhrase, variant: .curve25519, userIds: [userId]) - try await(self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user)) + try awaitPromise(self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user)) try self.storePrvs(prvs: [encryptedPrv.key], passPhrase: passPhrase, source: .generated) let updateKey = self.attester.updateKey( @@ -323,12 +323,12 @@ extension SetupViewController { pubkey: encryptedPrv.key.public, token: self.storage.token ) - try await(self.alertAndSkipOnRejection( + try awaitPromise(self.alertAndSkipOnRejection( updateKey, fail: "Failed to submit Public Key") ) let testWelcome = self.attester.testWelcome(email: userId.email, pubkey: encryptedPrv.key.public) - try await(self.alertAndSkipOnRejection( + try awaitPromise(self.alertAndSkipOnRejection( testWelcome, fail: "Failed to send you welcome email") ) @@ -350,7 +350,7 @@ extension SetupViewController { return Promise { let strength = try self.core.zxcvbnStrengthBar(passPhrase: passPhrase) guard strength.word.pass else { throw AppErr.user("Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase.") } - let confirmPassPhrase = try await(self.awaitUserPassPhraseEntry(title: "Confirm Pass Phrase")) + let confirmPassPhrase = try awaitPromise(self.awaitUserPassPhraseEntry(title: "Confirm Pass Phrase")) guard confirmPassPhrase != nil else { throw AppErr.silentAbort } guard confirmPassPhrase == passPhrase else { throw AppErr.user("Pass phrases don't match") } } @@ -376,7 +376,11 @@ extension SetupViewController { } private func handleButtonPressed() { - // TODO: - ANTON - show hud + // ignore if we are still fetching keys + guard !state.isSearchingBackups else { + return + } + view.endEditing(true) guard let passPhrase = passPhrase else { return } diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift index 8becd66ca..4ffb216cf 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift @@ -71,6 +71,7 @@ final class MyMenuViewController: ASDKViewController { super.init(node: ASDisplayNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -97,7 +98,6 @@ final class MyMenuViewController: ASDKViewController { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - guard #available(iOS 13.0, *) else { return } tableNode.reloadData() } } @@ -276,7 +276,6 @@ extension MyMenuViewController { divider.backgroundColor = decorator.dividerColor } } - } // MARK: - Actions diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift index 4a2ef12ca..0f291719a 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import FlowCryptUI import FlowCryptCommon +import FlowCryptUI import UIKit protocol MyMenuViewDecoratorType { diff --git a/FlowCrypt/Controllers/SignIn Other/EmailProviderViewController.swift b/FlowCrypt/Controllers/SignIn Other/EmailProviderViewController.swift index ec415e510..791273b7b 100644 --- a/FlowCrypt/Controllers/SignIn Other/EmailProviderViewController.swift +++ b/FlowCrypt/Controllers/SignIn Other/EmailProviderViewController.swift @@ -46,6 +46,7 @@ final class EmailProviderViewController: TableNodeViewController { node.dataSource = self } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -480,8 +481,8 @@ extension EmailProviderViewController { } Promise { - try await(self.imap.connectImap(session: imapSessionToCheck)) - try await(self.imap.connectSmtp(session: smtpSession)) + try awaitPromise(self.imap.connectImap(session: imapSessionToCheck)) + try awaitPromise(self.imap.connectSmtp(session: smtpSession)) } .then(on: .main) { [weak self] in self?.handleSuccessfulConnection() @@ -511,7 +512,6 @@ extension EmailProviderViewController { return .success(user) } - } // MARK: - Picker diff --git a/FlowCrypt/Controllers/SignIn Other/EmailProviderViewDecorator.swift b/FlowCrypt/Controllers/SignIn Other/EmailProviderViewDecorator.swift index fbbc39479..613a0a104 100644 --- a/FlowCrypt/Controllers/SignIn Other/EmailProviderViewDecorator.swift +++ b/FlowCrypt/Controllers/SignIn Other/EmailProviderViewDecorator.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit import FlowCryptUI +import UIKit protocol EmailProviderViewDecoratorType { var connectButtonTitle: NSAttributedString { get } diff --git a/FlowCrypt/Controllers/SignIn/SignInViewController.swift b/FlowCrypt/Controllers/SignIn/SignInViewController.swift index 95f5a2c90..79ae91558 100644 --- a/FlowCrypt/Controllers/SignIn/SignInViewController.swift +++ b/FlowCrypt/Controllers/SignIn/SignInViewController.swift @@ -20,6 +20,8 @@ final class SignInViewController: TableNodeViewController { private let core: Core private let decorator: SignInViewDecoratorType + private lazy var logger = Logger.nested(Self.self) + init( globalRouter: GlobalRouterType = GlobalRouter(), core: Core = Core.shared, @@ -34,6 +36,7 @@ final class SignInViewController: TableNodeViewController { node.dataSource = self } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -114,7 +117,8 @@ extension SignInViewController { showToast("Outlook sign in not implemented yet") // below for debugging do { - let start = DispatchTime.now() + let trace = Trace(id: "sign in outlook") + let keys = [ PrvKeyInfo( private: TestData.k3rsa4096.prv, @@ -133,13 +137,11 @@ extension SignInViewController { msgPwd: nil, isEmail: false ) - debugPrint(decrypted) - debugPrint("decrypted \(start.millisecondsSince)") + logger.logInfo("\(decrypted) - duration \(trace.finish())") } catch CoreError.exception { - debugPrint("catch exception") + logger.logError("catch exception") } catch { - debugPrint("catch generic") - debugPrint(error) + logger.logInfo("catch generic \(error)") } } @@ -150,10 +152,6 @@ extension SignInViewController { private func handle(option: SignInViewController.AppLinks) { guard let url = option.url else { assertionFailure("Issue in provided url"); return } - if #available(iOS 13.0, *) { - present(WebViewController(url: url), animated: true, completion: nil) - } else { - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } + present(WebViewController(url: url), animated: true, completion: nil) } } diff --git a/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift b/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift index 84d852006..99a487481 100644 --- a/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift +++ b/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift @@ -49,7 +49,7 @@ extension SigninButtonNode.Input { enum UserSignInType { case gmail, outlook, other - fileprivate var title: String { + private var title: String { switch self { case .gmail: return "sign_in_gmail".localized case .outlook: return "sign_in_outlook".localized diff --git a/FlowCrypt/Core/Core.swift b/FlowCrypt/Core/Core.swift index f9c426664..ec4bf230d 100644 --- a/FlowCrypt/Core/Core.swift +++ b/FlowCrypt/Core/Core.swift @@ -23,6 +23,8 @@ final class Core { private dynamic var started = false private dynamic var ready = false + private lazy var logger = Logger.nested(in: Self.self, with: "Js") + private init() {} public func version() throws -> CoreRes.Version { @@ -104,12 +106,15 @@ final class Core { started = true DispatchQueue.global(qos: .default).async { [weak self] in guard let self = self else { return } - let start = DispatchTime.now() + let trace = Trace(id: "Start in background") let jsFile = Bundle(for: Core.self).path(forResource: "flowcrypt-ios-prod.js.txt", ofType: nil)! let jsFileSrc = try? String(contentsOfFile: jsFile) self.context = JSContext(virtualMachine: self.vm)! self.context?.setObject(CoreHost(), forKeyedSubscript: "coreHost" as (NSCopying & NSObjectProtocol)) - self.context!.exceptionHandler = { _, exception in debugPrint("Js.exception: \(String(describing: exception))") } + self.context!.exceptionHandler = { [weak self] _, exception in + guard let exception = exception else { return } + self?.logger.logWarning("\(exception)") + } self.context!.evaluateScript("const APP_VERSION = 'iOS 0.2';") self.context!.evaluateScript(jsFileSrc) self.jsEndpointListener = self.context!.objectForKeyedSubscript("handleRequestFromHost") @@ -117,7 +122,7 @@ final class Core { let cb_last_value_filler: @convention(block) ([NSObject]) -> Void = { values in self.cb_last_value = values } self.context!.setObject(unsafeBitCast(cb_last_value_filler, to: AnyObject.self), forKeyedSubscript: "engine_host_cb_catcher" as (NSCopying & NSObjectProtocol)?) self.ready = true - debugPrint("JsContext took \(start.millisecondsSince)ms to start") + self.logger.logInfo("JsContext took \(trace.finish()) to start") } } } @@ -155,7 +160,7 @@ final class Core { let error = try? resJsonData.decodeJson(as: CoreRes.Error.self) if error != nil { let errMsg = "------ js err -------\nCore \(endpoint):\n\(error!.error.message)\n\(error!.error.stack ?? "no stack")\n------- end js err -----" - debugPrint(errMsg) + logger.logError(errMsg) throw CoreError.exception(errMsg) } return RawRes(json: resJsonData, data: Data(rawResponse[(separatorIndex + 1)...])) diff --git a/FlowCrypt/Core/CoreHost.swift b/FlowCrypt/Core/CoreHost.swift index b89470fe7..f6d435bff 100644 --- a/FlowCrypt/Core/CoreHost.swift +++ b/FlowCrypt/Core/CoreHost.swift @@ -2,13 +2,13 @@ // © 2017-2019 FlowCrypt Limited. All rights reserved. // +import BigInt import CommonCrypto // for hashing import Foundation import IDZSwiftCommonCrypto // for aes import JavaScriptCore // for export to js import Security // for rng import SwiftyRSA // for rsa -import BigInt @objc protocol CoreHostExports: JSExport { // crypto @@ -18,8 +18,6 @@ import BigInt func verifyRsaModPow(_ base: String, _ exponent: String, _ modulo: String) -> String func produceHashedIteratedS2k(_ algo: String, _ prefix: [UInt8], _ salt: [UInt8], _ passphrase: [UInt8], _ count: Int) -> [UInt8] - // other - func log(_ text: String) -> Void func setTimeout(_ callback: JSValue, _ ms: Double) -> String func clearTimeout(_ identifier: String) } @@ -31,10 +29,6 @@ final class CoreHost: NSObject, CoreHostExports { // -> a) reading rsa4096 prv key (just openpgp.key.readArmored(...)) takes 70ms. It should take about 10 ms. Could dearmor it in swift, return bytes // -> b) produceHashedIteratedS2k below takes 300ms for two keys, could be 100ms or so - func log(_ message: String) { - debugPrint(message.split(separator: "\n").map { "Js: \($0)" }.joined(separator: "\n")) - } - // brings total decryption time from 200->30ms (rsa2048), 3900->420ms (rsa4096) func decryptRsaNoPadding(_ rsaPrvDerBase64: String, _ encryptedBase64: String) -> String { do { @@ -43,8 +37,7 @@ final class CoreHost: NSObject, CoreHostExports { let decrypted = try rsaEncrypted.decrypted(with: rsaPrv, padding: .NONE) return decrypted.base64String } catch { - debugPrint("decryptRsaNoPadding error") - debugPrint(error) + Logger.logError("decryptRsaNoPadding error \(error)") return "" } } diff --git a/FlowCrypt/Core/CoreTypes.swift b/FlowCrypt/Core/CoreTypes.swift index 07afec6a7..49a0ccd22 100644 --- a/FlowCrypt/Core/CoreTypes.swift +++ b/FlowCrypt/Core/CoreTypes.swift @@ -186,9 +186,3 @@ struct MsgBlock: Decodable { // case cryptupVerification; // not sure if Swift code will ever encounter this } } - -extension MsgBlock { - var isAttachmentBlock: Bool { - type == .plainAtt || type == .encryptedAtt || type == .decryptedAtt - } -} diff --git a/FlowCrypt/Core/Models/CoreTypesTest.swift b/FlowCrypt/Core/Models/CoreTypesTest.swift index b61c0b883..317d845e8 100644 --- a/FlowCrypt/Core/Models/CoreTypesTest.swift +++ b/FlowCrypt/Core/Models/CoreTypesTest.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import XCTest import FlowCryptCommon +import XCTest class CoreTypesTest: XCTestCase { @@ -59,7 +59,6 @@ class CoreTypesTest: XCTestCase { 1, "If the [KeyDetails] contains two keys with the same fingerprint, only one should be added" ) - } func test_key_ids_with_same_fingerprint() { diff --git a/FlowCrypt/Core/Models/KeyDetails.swift b/FlowCrypt/Core/Models/KeyDetails.swift index 1a7ab442d..c98d20021 100644 --- a/FlowCrypt/Core/Models/KeyDetails.swift +++ b/FlowCrypt/Core/Models/KeyDetails.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Foundation import FlowCryptCommon +import Foundation struct KeyDetails: Decodable { let `public`: String diff --git a/FlowCrypt/Extensions/UIViewControllerExtensions.swift b/FlowCrypt/Extensions/UIViewControllerExtensions.swift index b438b9312..7f314850f 100644 --- a/FlowCrypt/Extensions/UIViewControllerExtensions.swift +++ b/FlowCrypt/Extensions/UIViewControllerExtensions.swift @@ -108,6 +108,11 @@ extension UIViewController { func showSpinner(_ message: String = "loading_title".localized, isUserInteractionEnabled: Bool = false) { DispatchQueue.main.async { + guard self.view.subviews.first(where: { $0 is MBProgressHUD }) == nil else { + // hud is already shown + return + } + let spinner = MBProgressHUD.showAdded(to: self.view, animated: true) spinner.label.text = message spinner.isUserInteractionEnabled = isUserInteractionEnabled @@ -126,7 +131,7 @@ extension UIViewController { return Promise { [weak self] resolve, _ in guard let self = self else { throw AppErr.nilSelf } do { - _ = try await(promise) + _ = try awaitPromise(promise) resolve(()) } catch { DispatchQueue.main.async { diff --git a/FlowCrypt/Extensions/URLSessionExtension.swift b/FlowCrypt/Extensions/URLSessionExtension.swift index 56818d7d2..38d7475d9 100644 --- a/FlowCrypt/Extensions/URLSessionExtension.swift +++ b/FlowCrypt/Extensions/URLSessionExtension.swift @@ -17,17 +17,20 @@ struct HttpErr: Error { let error: Error? } +private let logger = Logger.nested("URLSession") + extension URLSession { func call(_ urlRequest: URLRequest, tolerateStatus: [Int]? = nil) -> Promise { return Promise { resolve, reject in - let start = DispatchTime.now() + let trace = Trace(id: "call") self.dataTask(with: urlRequest) { data, response, error in let res = response as? HTTPURLResponse let status = res?.statusCode ?? GeneralConstants.Global.generalError let urlMethod = urlRequest.httpMethod ?? "GET" let urlString = urlRequest.url?.absoluteString ?? "??" - let message = "URLSession.call status:\(status) ms:\(start.millisecondsSince) \(urlMethod) \(urlString)" - debugPrint(message) + let message = "URLSession.call status:\(status) ms:\(trace.finish()) \(urlMethod) \(urlString)" + Logger.nested("URLSession").logInfo(message) + let validStatusCode = 200 ... 299 let isInToleranceStatusCodes = (tolerateStatus?.contains(status) ?? false) let isCodeValid = validStatusCode ~= status || isInToleranceStatusCodes @@ -47,7 +50,7 @@ extension URLSession { guard url != nil else { throw HttpErr(status: -2, data: Data(), error: AppErr.unexpected("Invalid url: \(urlStr)")) } - return try await(self.call(URLRequest(url: url!), tolerateStatus: tolerateStatus)) + return try awaitPromise(self.call(URLRequest(url: url!), tolerateStatus: tolerateStatus)) } } } diff --git a/FlowCrypt/Functionality/DataManager/DataService.swift b/FlowCrypt/Functionality/DataManager/DataService.swift index 6ac9cc32b..e162227df 100644 --- a/FlowCrypt/Functionality/DataManager/DataService.swift +++ b/FlowCrypt/Functionality/DataManager/DataService.swift @@ -135,13 +135,7 @@ extension DataService: KeyDataServiceType { extension DataService: DBMigration { /// Perform all kind of migrations func performMigrationIfNeeded() -> Promise { - return Promise { [weak self] in - guard let self = self else { throw AppErr.nilSelf } - // migrate to encrypted storage - try await(self.encryptedStorage.performMigrationIfNeeded()) - // migrate all other type of migrations - try await(self.migrationService.performMigrationIfNeeded()) - } + migrationService.performMigrationIfNeeded() } } diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift index 2df04bb99..47ddcf5fb 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift @@ -11,12 +11,11 @@ import Foundation import Promises import RealmSwift -protocol EncryptedStorageType: DBMigration { +protocol EncryptedStorageType { var storage: Realm { get } func addKeys(keyDetails: [KeyDetails], passPhrase: String, source: KeySource) func updateKeys(keyDetails: [KeyDetails], passPhrase: String, source: KeySource) - func currentToken() -> String? func publicKey() -> String? func keys() -> Results? @@ -28,30 +27,44 @@ protocol EncryptedStorageType: DBMigration { } final class EncryptedStorage: EncryptedStorageType { - enum Constants { - // Encrypted schema version - static let schemaVersion: UInt64 = 1 - // User object added to schema - static let schemaVersionUser: UInt64 = 2 - // Account field added to Keys - static let schemaVersionMultipleAccounts: UInt64 = 3 + private struct SchemaVersion { + /// specify app version when schema was applied + let appVersion: String + /// current schema version + let dbSchemaVersion: UInt64 + } + + // new schema should be added as a new case + private enum EncryptedStorageSchema: CaseIterable { + case initial + var version: SchemaVersion { + switch self { + case .initial: + return SchemaVersion(appVersion: "0.2.0", dbSchemaVersion: 1) + } + } + } + + private enum Constants { static let encryptedDbFilename = "encrypted.realm" } private let keychainService: KeyChainServiceType - private let fileManager: FileManager private var realmKey: Data { keychainService.getStorageEncryptionKey() } - // TODO: - ANTON - Add logger (https://github.com/FlowCrypt/flowcrypt-ios/issues/282) - private let debugLabel = "[EncryptedStorage][DB Migration]" + private lazy var migrationLogger = Logger.nested(in: Self.self, with: .migration) + + private let currentSchema: EncryptedStorageSchema = .initial + private let supportedSchemas = EncryptedStorageSchema.allCases private var encryptedConfiguration: Realm.Configuration { let path = getDocumentDirectory() + "/" + Constants.encryptedDbFilename - let latestSchemaVersion = Constants.schemaVersionMultipleAccounts + let latestSchemaVersion = currentSchema.version.dbSchemaVersion + return Realm.Configuration( fileURL: URL(fileURLWithPath: path), encryptionKey: realmKey, @@ -74,13 +87,16 @@ final class EncryptedStorage: EncryptedStorageType { } } - init( - fileManager: FileManager = .default, - keychainHelper: KeyChainServiceType = KeyChainService() - ) { - self.fileManager = fileManager + init(keychainHelper: KeyChainServiceType = KeyChainService()) { self.keychainService = KeyChainService() } + + private func getDocumentDirectory() -> String { + guard let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { + fatalError("No path direction for .documentDirectory") + } + return documentDirectory + } } // MARK: - LogOut @@ -107,117 +123,31 @@ extension EncryptedStorage: LogOutHandler { private func destroyEncryptedStorage() { cleanup() - destroyPlainConfigurationIfNeeded() - } - - /// Remove configuration if user still on plain realm - private func destroyPlainConfigurationIfNeeded() { - guard let defaultPath = Realm.Configuration.defaultConfiguration.fileURL else { - return - } - guard defaultPath != self.encryptedConfiguration.fileURL else { - return - } - guard fileManager.fileExists(atPath: defaultPath.absoluteString) else { - return - } - - do { - try fileManager.removeItem(at: defaultPath) - } catch { - fatalError("Could not delete configuration for \(defaultPath) with error: \(error)") - } } } -// MARK: - Migration +// MARK: - Schema migration extension EncryptedStorage { - func performMigrationIfNeeded() -> Promise { - // current migration only does plain realm -> encrypted realm migration, with no database schema change - // during next future migration, we can delete this and only focus on database schema migration - let documentDirectory = getDocumentDirectory() - let plainRealmPath = documentDirectory + "/default.realm" - let encryptedRealmPath = documentDirectory + "/" + Constants.encryptedDbFilename - guard fileManager.fileExists(atPath: plainRealmPath) else { - debugPrint("Migration not needed: plain realm not used") - return Promise(()) - } - guard !fileManager.fileExists(atPath: encryptedRealmPath) else { - debugPrint("Migration not needed: encrypted realm already set up") - return Promise(()) - } - debugPrint("Performing migration from plain to encrypted Realm") - guard let plainRealm = try? Realm(configuration: Realm.Configuration.defaultConfiguration) else { - debugPrint("Failed to load plain realm, although the db file was present: destroying") - destroyEncryptedStorage() // destroys plain as well as encrypted realm (if one existed) - return Promise(()) - } - // write encrypted copy of plain realm db - // encryptionKey is for the NEW copy - try! plainRealm.writeCopy(toFile: URL(fileURLWithPath: encryptedRealmPath), encryptionKey: realmKey) - // launch configuration and perform schema migration if needed - return Promise { [weak self] resolve, reject in - guard let self = self else { throw AppErr.nilSelf } - let configuration = Realm.Configuration( - fileURL: URL(fileURLWithPath: encryptedRealmPath), - encryptionKey: self.realmKey, - schemaVersion: Constants.schemaVersion, - migrationBlock: { migration, oldSchemaVersion in - do { - debugPrint("oldSchemaVersion \(oldSchemaVersion)") - debugPrint("Performing migration \(migration)") - // I'd rather the app crashes then to pretend it has removed the plain copy - // todo - remove the following line for migrations from 0.1.7 up - try self.fileManager.removeItem(atPath: plainRealmPath) // delete previous configuration - resolve(()) - } catch { - reject(error) - } - } - ) - _ = try Realm(configuration: configuration) // runs migration and calls completion block - } - } - - private func getDocumentDirectory() -> String { - guard let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { - fatalError("No path direction for .documentDirectory") - } - return documentDirectory - } - private func performSchemaMigration(migration: Migration, from oldSchemaVersion: UInt64, to newVersion: UInt64) { - debugPrint("\(debugLabel) Check if migration needed from \(oldSchemaVersion) to \(newVersion)") + migrationLogger.logInfo("Check if migration needed from \(oldSchemaVersion) to \(newVersion)") guard oldSchemaVersion < newVersion else { - debugPrint("\(debugLabel) Migration not needed") + migrationLogger.logInfo("Migration not needed") return } - switch newVersion { - case 0, Constants.schemaVersion, Constants.schemaVersionUser: - debugPrint("\(debugLabel) Schema Migration not needed") - case Constants.schemaVersionMultipleAccounts: - performMultipleAccount(migration: migration) - default: - assertionFailure("\(debugLabel) Migration is not implemented for this schema version") + supportedSchemas.forEach { + switch $0 { + case .initial: + migrationLogger.logInfo("Schema migration not needed for initial schema") +// case .someNewSchema: +// performSomeNewSchema(migration: migration) + } } } - private func performMultipleAccount(migration: Migration) { - debugPrint("\(debugLabel) Start Multiple account migration") - - debugPrint("\(debugLabel) - Set isActive = true for a user") - migration.enumerateObjects(ofType: String(describing: UserObject.self)) { (_, newUser) in - newUser?["isActive"] = true - } - - debugPrint("\(debugLabel) - Add account to key") - migration.enumerateObjects(ofType: String(describing: KeyInfo.self)) { (_, newKey) in - migration.enumerateObjects(ofType: String(describing: UserObject.self)) { (user, _) in - newKey?["account"] = user?["email"] ?? "" - } - } + private func performSomeNewSchema(migration: Migration) { + migrationLogger.logInfo("Start Multiple account migration") } } @@ -261,14 +191,6 @@ extension EncryptedStorage { } } -// MARK: - Token -extension EncryptedStorage { - @available(*, deprecated, message: "Use information from UserObject") - func currentToken() -> String? { - storage.objects(EmailAccessToken.self).first?.value - } -} - // MARK: - User extension EncryptedStorage { var activeUser: UserObject? { @@ -296,7 +218,7 @@ extension EncryptedStorage { try storage.write { storage.deleteAll() } - } catch let error { + } catch { assertionFailure("Error while deleting the objects from the storage \(error)") } } diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/KeyChainService.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/KeyChainService.swift index 39be68ca4..9deec38a7 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/KeyChainService.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/KeyChainService.swift @@ -6,9 +6,9 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // +import FlowCryptCommon import Foundation import Security -import FlowCryptCommon // keychain is used to generate and retrieve encryption key which is used to encrypt local DB // it does not contain any actual data or keys other than the db encryption key @@ -18,6 +18,8 @@ protocol KeyChainServiceType { } struct KeyChainService: KeyChainServiceType { + private static var logger = Logger.nested(in: Self.self, with: "Keychain") + // the prefix ensures that we use a different keychain index after deleting the app // because keychain entries survive app uninstall private static var keychainIndex: String = { @@ -33,7 +35,8 @@ struct KeyChainService: KeyChainServiceType { let prefix = Data(randomBytes) .base64EncodedString() .replacingOccurrences(of: "[^A-Za-z0-9]+", with: "", options: [.regularExpression]) - debugPrint("LocalStorage.secureKeychainPrefix generating new: \(prefix)") + + logger.logInfo("LocalStorage.secureKeychainPrefix generating new: \(prefix)") UserDefaults.standard.set(prefix, forKey: prefixStorageIndex) return prefix + storageEncryptionKeyIndexSuffix }() @@ -41,7 +44,8 @@ struct KeyChainService: KeyChainServiceType { private let keyByteLen = 64 private func generateAndSaveStorageEncryptionKey() { - debugPrint("KeyChainService->generateAndSaveStorageEncryptionKey") + Self.logger.logInfo("generateAndSaveStorageEncryptionKey") + guard let randomBytes = CoreHost().getSecureRandomByteNumberArray(keyByteLen) else { fatalError("KeyChainServiceType generateAndSaveStorageEncryptionKey getSecureRandomByteNumberArray bytes are nil") } diff --git a/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift b/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift index e83f7dbf8..228b749e9 100644 --- a/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift @@ -51,6 +51,5 @@ extension LocalStorage: LogOutHandler { .forEach { storage.removeObject(forKey: $0) } - } } diff --git a/FlowCrypt/Functionality/DataManager/UserAccountService.swift b/FlowCrypt/Functionality/DataManager/UserAccountService.swift index c6ca3e487..66d06a87e 100644 --- a/FlowCrypt/Functionality/DataManager/UserAccountService.swift +++ b/FlowCrypt/Functionality/DataManager/UserAccountService.swift @@ -24,6 +24,8 @@ final class UserAccountService { private let imap: Imap private let googleService: GoogleUserService + private lazy var logger = Logger.nested(Self.self) + init( encryptedStorage: EncryptedStorageType & LogOutHandler = EncryptedStorage(), localStorage: LocalStorageType & LogOutHandler = LocalStorage(), @@ -84,7 +86,7 @@ extension UserAccountService: UserAccountServiceType { .first(where: { $0.email == user.email }) guard let userObject = userObj else { - debugPrint("[UserAccountService] UserObject should be persisted to encrypted storage") + logger.logWarning("UserObject should be persisted to encrypted storage in case of switching accounts") return nil } @@ -101,7 +103,7 @@ extension UserAccountService: UserAccountServiceType { case .password: sessionType = .session(userObject) case .none: - debugPrint("[UserAccountService] authType is not defined") + logger.logWarning("authType is not defined in switchActiveSession") return nil } @@ -112,7 +114,7 @@ extension UserAccountService: UserAccountServiceType { private func logOutCurrentUser() { guard let email = dataService.currentUser?.email else { - debugPrint("[UserAccountService] user is not logged in") + logger.logWarning("User is not logged in. Can't log out") return } @@ -122,19 +124,19 @@ extension UserAccountService: UserAccountServiceType { case .password: imap.disconnect() default: - debugPrint("[UserAccountService] currentAuthType is not resolved") + logger.logWarning("currentAuthType is not resolved") } do { try self.storages.forEach { try $0.logOutUser(email: email) } - } catch let error { - debugPrint("[UserAccountService] storage error \(error)") + } catch { + logger.logError("storage error \(error)") } } /// cleanup all user sessions func cleanup() { - debugPrint("[UserAccountService] Clean up storages") + logger.logInfo("Clean up storages") encryptedStorage.cleanup() localStorage.cleanup() } diff --git a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift index 1398ae5a5..49d7ffab6 100644 --- a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift @@ -15,7 +15,7 @@ extension UIViewController { let isErrorHandled = composedHandler.handle(error: error, for: self) if !isErrorHandled { - debugPrint("[ERROR HANDLING] - ErrorHandler should be used for this error ***** \(error)") + Logger.nested("ERROR HANDLING").logInfo("ErrorHandler should be used for this error ***** \(error)") } return isErrorHandled diff --git a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Gmail+Backup.swift b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Gmail+Backup.swift index 3244b7803..753b04a16 100644 --- a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Gmail+Backup.swift +++ b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Gmail+Backup.swift @@ -6,34 +6,37 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Promises -import GTMSessionFetcher import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: BackupProvider { func searchBackups(for email: String) -> Promise { - return Promise { (resolve, _) in + Logger.logVerbose("[GmailService] will begin searching for backups") + return Promise { resolve, _ in let backupSearchExpressions = GeneralConstants.EmailConstant .recoverAccountSearchSubject .map { searchExpression(using: MessageSearchContext(expression: $0)) } - let backupMessages = try await(all(backupSearchExpressions)) + Logger.logVerbose("[GmailService] searching with \(backupSearchExpressions.count) search expressions") + let backupMessages = try awaitPromise(all(backupSearchExpressions)) .flatMap { $0 } + Logger.logVerbose("[GmailService] searching done, found \(backupMessages.count) backup messages") let uniqueMessages = Set(backupMessages) let attachments = uniqueMessages .compactMap { (message) -> [(String, String)]? in + Logger.logVerbose("[GmailService] processing backup '\(message.subject ?? "-")' with \(message.attachmentIds.count) attachments") guard let identifier = message.identifier.stringId else { + Logger.logVerbose("[GmailService] skipping this last backup?") return nil } return message.attachmentIds.map { (identifier, $0) } } .flatMap { $0 } .map(findAttachment) - - // TODO: - TOM 1 - // Here I'm getting the correct number of attachments with backups (17 for cryptup.tester@gmail.com account) - - let data = try await(all(attachments)).joined + Logger.logVerbose("[GmailService] downloading \(attachments.count) attachments with possible backups in them") + let data = try awaitPromise(all(attachments)).joined + Logger.logVerbose("[GmailService] downloaded \(attachments.count) attachments that contain \(data.count / 1024)kB of data") resolve(data) } } @@ -44,8 +47,8 @@ extension GmailService: BackupProvider { messageId: context.messageId, identifier: context.attachmentId ) - return Promise { (resolve, reject) in - self.gmailService.executeQuery(query) { (_, data, error) in + return Promise { resolve, reject in + self.gmailService.executeQuery(query) { _, data, error in if let error = error { reject(GmailServiceError.providerError(error)) return diff --git a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift index 11694e6f7..8f0727620 100644 --- a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift +++ b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift @@ -24,7 +24,7 @@ extension Imap: BackupProvider { func searchBackups(for email: String) -> Promise { return Promise { [weak self] () -> Data in guard let self = self else { throw AppErr.nilSelf } - var folderPaths = try await(self.fetchFolders()) + var folderPaths = try awaitPromise(self.fetchFolders()) .compactMap { $0.path } guard folderPaths.isNotEmpty else { @@ -38,7 +38,7 @@ extension Imap: BackupProvider { let searchExpr = self.createSearchBackupExpression(for: email) let uidsForFolders = try folderPaths.compactMap { folder -> UidsContext in - let uids = try await(self.fetchUids(folder: folder, expr: searchExpr)) + let uids = try awaitPromise(self.fetchUids(folder: folder, expr: searchExpr)) return UidsContext(path: folder, uids: uids) } @@ -47,7 +47,7 @@ extension Imap: BackupProvider { } let messageContexts = try uidsForFolders.flatMap { uidsContext -> [MsgContext] in - let msgs = try await(self.fetchMessagesIn(folder: uidsContext.path, uids: uidsContext.uids)) + let msgs = try awaitPromise(self.fetchMessagesIn(folder: uidsContext.path, uids: uidsContext.uids)) return msgs.map { msg in MsgContext(path: uidsContext.path, msg: msg) } } @@ -65,7 +65,7 @@ extension Imap: BackupProvider { } let dataArr = try attContext.map { attContext -> Data in - try await(self.fetchMsgAttribute( + try awaitPromise(self.fetchMsgAttribute( in: attContext.path, msgUid: attContext.msg.uid, part: attContext.part diff --git a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift index c91afad72..eb208ff23 100644 --- a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/CloudContactsProvider.swift @@ -61,7 +61,7 @@ extension UserContactsProvider: CloudContactsProvider { } return Promise<[String]> { () -> [String] in - let response = try await(URLSession.shared.call(URLRequest(url: url))) + let response = try awaitPromise(URLSession.shared.call(URLRequest(url: url))) let emails = try JSONDecoder().decode(GoogleContactsResponse.self, from: response.data).emails return emails } diff --git a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift index ff5d4a014..ec271ff57 100644 --- a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift @@ -13,12 +13,12 @@ struct GmailService: MailServiceProvider { let mailServiceProviderType = MailServiceProviderType.gmail let userService: GoogleUserService + let logger = Logger.nested("GmailService") var gmailService: GTLRService { let service = GTLRGmailService() if userService.authorization == nil { - // logLevel = error - debugPrint("[GmailService] authorization for current user is nil") + logger.logWarning("authorization for current user is nil") } service.authorizer = userService.authorization diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift index 5462bd116..f4d7c4be9 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+Other.swift @@ -18,16 +18,15 @@ extension Imap { Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } - let start = DispatchTime.now() let kind = self.messageKindProvider.imapMessagesRequestKind guard uids.count() > 0 else { - log("fetchMsgs_empty", error: nil, res: [], start: start) + self.logger.logError("Empty messages fetched") resolve([]) // attempting to fetch an empty set of uids would cause IMAP error return } - let messages = try await(self.fetchMessage(in: folder, kind: kind, uids: uids)) + let messages = try awaitPromise(self.fetchMessage(in: folder, kind: kind, uids: uids)) resolve(messages) } } diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+msg.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+msg.swift index d3b70ccbe..a237586c5 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+msg.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+msg.swift @@ -16,4 +16,3 @@ extension Imap { } } } - diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift index 8fabeea75..7abc4e2ad 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift @@ -18,7 +18,8 @@ extension Imap { start: DispatchTime = DispatchTime.now() ) -> (Error?, T?) -> Void { return { [weak self] error, res in - log(op, error: error, res: res, start: start) + self?.logger.logError("Error \(String(describing: error))") +// log(op, error: error, res: res, start: start) guard self?.notRetrying(op, error, resolve, reject, retry: retry) ?? false else { return } if let res = res { resolve(res) @@ -36,7 +37,7 @@ extension Imap { ) -> (Error?) -> Void { let start = DispatchTime.now() return { [weak self] error in - log(op, error: error, res: nil, start: start) + self?.logger.logError("Error \(String(describing: error))") guard self?.notRetrying(op, error, resolve, reject, retry: retry) ?? false else { return } if let error = error { @@ -55,7 +56,7 @@ extension Imap { ) -> (Error?, Any?) -> Void { let start = DispatchTime.now() return { [weak self] error, _ in - log(op, error: error, res: nil, start: start) + self?.logger.logError("Error \(String(describing: error))") guard self?.notRetrying(op, error, resolve, reject, retry: retry) ?? false else { return } if let error = error { reject(error) @@ -81,37 +82,37 @@ extension Imap { switch error { case .authentication: if let operation = lastErr[op], operation == error { return true } - logDebug(3, "(\(debugId)|\(op)) it's a retriable auth err, will call renewAccessToken") +// logger.logInfo("it's a retriable auth err, will call renewAccessToken \(op)") lastErr[op] = error - renewSession().then { _ in - logDebug(5, "(\(debugId)|\(op)) forced session refreshes") - log("renewAccessToken for \(op), will retry \(op)", error: nil, res: "", start: start) - retry().then(resolve).catch(reject) - }.catch(reject) - logDebug(7, "(\(debugId)|\(op)) just set lastErr to ", value: lastErr[op]) - logDebug(11, "(\(debugId)|\(op)) return=true (need to retry)") + renewSession() + .then { _ in +// self?.logger.logInfo("forced session refreshes \(op)") + retry().then(resolve).catch(reject) + } + .catch(reject) +// logger.logDebug("just set lastErr to \(lastErr[op])") return false case .connection: if let operation = lastErr[op], operation == error { return true } - logDebug(13, "(\(debugId)|\(op)) it's a retriable conn err, clear sessions") +// logDebug(13, "(\(debugId)|\(op)) it's a retriable conn err, clear sessions") imapSess = nil // the connection has dropped, so it's probably ok to not officially "close" it smtpSess = nil // but maybe there could be a cleaner way to dispose of the connection? lastErr[op] = error - logDebug(14, "(\(debugId)|\(op)) just set lastErr to ", value: lastErr[op]) - log("conn drop for \(op), cleared sessions, will retry \(op)", error: nil, res: nil, start: start) +// logDebug(14, "(\(debugId)|\(op)) just set lastErr to ", value: lastErr[op]) +// log("conn drop for \(op), cleared sessions, will retry \(op)", error: nil, res: nil, start: start) retry().then(resolve).catch(reject) - logDebug(15, "(\(debugId)|\(op)) return=true (need to retry)") +// logDebug(15, "(\(debugId)|\(op)) return=true (need to retry)") return false default: - logDebug(8, "(\(debugId)|\(op)) err not retriable, rejecting ", value: err) +// logDebug(8, "(\(debugId)|\(op)) err not retriable, rejecting ", value: err) reject(error) lastErr[op] = error - logDebug(9, "(\(debugId)|\(op)) just set lastErr to ", value: lastErr[op]) - logDebug(12, "(\(debugId)|\(op)) return=true (no need to retry)") +// logDebug(9, "(\(debugId)|\(op)) just set lastErr to ", value: lastErr[op]) +// logDebug(12, "(\(debugId)|\(op)) return=true (no need to retry)") return true } } else { diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift index 4a9efeea3..821d28a8e 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+session.swift @@ -6,10 +6,12 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // +import FlowCryptCommon import Foundation import Promises extension Imap { + func setupSession() { guard let imapSession = dataService.imapSession(), @@ -24,14 +26,14 @@ extension Imap { private func createNewConnection(imapSession: IMAPSession?, smtpSession: SMTPSession?) { if let imap = imapSession { - debugPrint("IMAP: creating a new session") + logger.logInfo("Creating a new IMAP session") let newImapSession = MCOIMAPSession(session: imap) imapSess = newImapSession //logIMAPConnection(for: imapSess!) } if let smtp = smtpSession { - debugPrint("SMTP: creating a new session") + logger.logInfo("Creating a new SMTP session") let newSmtpSession = MCOSMTPSession(session: smtp) smtpSess = newSmtpSession //logSMTPConnection(for: smtpSess!) @@ -39,16 +41,16 @@ extension Imap { } private func logIMAPConnection(for session: MCOIMAPSession) { - session.connectionLogger = { (connectionID, type, data) in + session.connectionLogger = { [weak self] connectionID, type, data in guard let data = data, let string = String(data: data, encoding: .utf8) else { return } - debugPrint("### IMAP:\(type):\(string)") + self?.logger.logInfo("connection IMAP :\(type):\(string)") } } private func logSMTPConnection(for smtpSession: MCOSMTPSession) { - smtpSession.connectionLogger = { (connectionID, type, data) in + smtpSession.connectionLogger = { [weak self] connectionID, type, data in guard let data = data, let string = String(data: data, encoding: .utf8) else { return } - debugPrint("### SMTP:\(type):\(string)") + self?.logger.logInfo("connection SMTP:\(type):\(string)") } } @@ -75,10 +77,15 @@ extension Imap { } func disconnect() { - let start = DispatchTime.now() - imapSess?.disconnectOperation().start { error in log("disconnect", error: error, res: nil, start: start) } + let start = Trace(id: "Imap disconnect") + imapSess?.disconnectOperation().start { [weak self] error in + if let error = error { + self?.logger.logError("disconnect with \(error)") + } else { + self?.logger.logInfo("disconnect with duration \(start.finish())") + } + } imapSess = nil smtpSess = nil // smtp session has no disconnect method } } - diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap.swift index 7466c90d1..61d6636fd 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap.swift @@ -22,6 +22,8 @@ final class Imap: MailServiceProvider { var lastErr: [String: AppErr] = [:] let dataService: Injection + lazy var logger = Logger.nested(Self.self) + private init( dataService: Injection = DataService.shared, helper: ImapHelperType = ImapHelper(), diff --git a/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SMTPSession.swift b/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SMTPSession.swift index 143914c9e..677212bb0 100644 --- a/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SMTPSession.swift +++ b/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SMTPSession.swift @@ -49,7 +49,7 @@ extension MCOSMTPSession { connectionType = type } - switch session.authType { + switch session.authType { case let .oAuthGmail(token): authType = .xoAuth2 oAuth2Token = token @@ -58,4 +58,3 @@ extension MCOSMTPSession { } } } - diff --git a/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SessionCredentialsProvider.swift b/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SessionCredentialsProvider.swift index 49538b86c..3ebc32d40 100644 --- a/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SessionCredentialsProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/Mail Sessions Providers/SessionCredentialsProvider.swift @@ -58,7 +58,7 @@ struct SessionCredentialsService: SessionCredentialsProvider { guard let email = email, let services = manager?.provider(forEmail: email)?.imapServices() as? [MCONetService], - let credentials = services.first(where: { $0.connectionType == MCOConnectionType(connection)}) + let credentials = services.first(where: { $0.connectionType == MCOConnectionType(connection) }) else { return error(for: connection) } @@ -79,7 +79,7 @@ struct SessionCredentialsService: SessionCredentialsProvider { guard let email = email, let services = manager?.provider(forEmail: email)?.smtpServices() as? [MCONetService], - let credentials = services.first(where: { $0.connectionType == MCOConnectionType(connection)}) + let credentials = services.first(where: { $0.connectionType == MCOConnectionType(connection) }) else { return error(for: connection) } diff --git a/FlowCrypt/Functionality/Mail Provider/MailProvider.swift b/FlowCrypt/Functionality/Mail Provider/MailProvider.swift index 9ec7b2fac..50cb43476 100644 --- a/FlowCrypt/Functionality/Mail Provider/MailProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/MailProvider.swift @@ -101,5 +101,3 @@ extension MailProvider { } } } - - diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift index 96c494705..aaefe76de 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift @@ -7,13 +7,13 @@ // import Foundation -import Promises import GoogleAPIClientForREST import GTMSessionFetcher +import Promises extension GmailService: MessageGateway { func sendMail(mime: Data) -> Promise { - Promise { (resolve, reject) in + Promise { resolve, reject in guard let raw = GTLREncodeBase64(mime) else { return reject(GmailServiceError.messageEncode) } @@ -27,7 +27,7 @@ extension GmailService: MessageGateway { uploadParameters: nil ) - self.gmailService.executeQuery(querySend) { (_, _, error) in + self.gmailService.executeQuery(querySend) { _, _, error in if let error = error { reject(GmailServiceError.providerError(error)) return diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift index c2b6f2954..fc68f966f 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift @@ -6,13 +6,13 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Promises -import GTMSessionFetcher import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: MessageProvider { func fetchMsg(message: Message, folder: String) -> Promise { - return Promise { (resolve, reject) in + return Promise { resolve, reject in guard let identifier = message.identifier.stringId else { return reject(GmailServiceError.missedMessageInfo("id")) } @@ -20,7 +20,7 @@ extension GmailService: MessageProvider { let query = GTLRGmailQuery_UsersMessagesGet.query(withUserId: .me, identifier: identifier) query.format = kGTLRGmailFormatRaw - self.gmailService.executeQuery(query) { (_, data, error) in + self.gmailService.executeQuery(query) { _, data, error in if let error = error { reject(GmailServiceError.providerError(error)) return diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift new file mode 100644 index 000000000..935240a6e --- /dev/null +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift @@ -0,0 +1,119 @@ +// +// MessageService.swift +// FlowCrypt +// +// Created by Anton Kharchevskyi on 12.05.2021. +// Copyright © 2021 FlowCrypt Limited. All rights reserved. +// + +import Foundation +import Promises + +// MARK: - MessageAttachment +struct MessageAttachment { + let name: String + let size: Int +} + +// MARK: - FetchedMessage +struct FetchedMessage { + enum MessageType { + case error, encrypted, plain + } + + let rawMimeData: Data + let text: String + let attachments: [MessageAttachment] + let messageType: MessageType +} + +extension FetchedMessage { + // TODO: - ANTON - fix with empty state for MessageViewController + static let empty = FetchedMessage( + rawMimeData: Data(), + text: "loading_title".localized + "...", + attachments: [], + messageType: .plain + ) +} + +// MARK: - MessageService +final class MessageService { + private let messageProvider: MessageProvider + private let dataService: DataServiceType & KeyDataServiceType + private let core: Core + + init( + messageProvider: MessageProvider = MailProvider.shared.messageProvider, + dataService: DataServiceType & KeyDataServiceType = DataService.shared, + core: Core = Core.shared + ) { + self.messageProvider = messageProvider + self.dataService = dataService + self.core = core + } + + func getMessage(with input: Message, folder: String) -> Promise { + Promise { [weak self] resolve, reject in + guard let self = self else { return } + + let rawMimeData = try awaitPromise( + self.messageProvider.fetchMsg(message: input, folder: folder) + ) + + guard let keys = self.dataService.keys else { + reject(CoreError.notReady("Could not fetch keys")) + return + } + + let decrypted = try self.core.parseDecryptMsg( + encrypted: rawMimeData, + keys: keys, + msgPwd: nil, + isEmail: true + ) + + let decryptErrBlocks = decrypted.blocks + .filter { $0.decryptErr != nil } + + let attachments = decrypted.blocks + .filter(\.isAttachmentBlock) + .map(MessageAttachment.init) + + let messageType: FetchedMessage.MessageType + let text: String + + if let decryptErrBlock = decryptErrBlocks.first { + let rawMsg = decryptErrBlock.content + let err = decryptErrBlock.decryptErr?.error + text = "Could not decrypt:\n\(err?.type.rawValue ?? "UNKNOWN"): \(err?.message ?? "??")\n\n\n\(rawMsg)" + messageType = .error + } else { + text = decrypted.text + messageType = decrypted.replyType == CoreRes.ReplyType.encrypted ? .encrypted : .plain + } + + let fetchedMessage = FetchedMessage( + rawMimeData: rawMimeData, + text: text, + attachments: attachments, + messageType: messageType + ) + + resolve(fetchedMessage) + } + } +} + +private extension MessageAttachment { + init(block: MsgBlock) { + self.name = block.attMeta?.name ?? "Attachment" + self.size = block.attMeta?.length ?? 0 + } +} + +private extension MsgBlock { + var isAttachmentBlock: Bool { + type == .plainAtt || type == .encryptedAtt || type == .decryptedAtt + } +} diff --git a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Gmail+MessageOperations.swift b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Gmail+MessageOperations.swift index 4ef75048c..00b311688 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Gmail+MessageOperations.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Gmail+MessageOperations.swift @@ -7,9 +7,9 @@ // import Foundation -import Promises -import GTMSessionFetcher import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: MessageOperationsProvider { func markAsRead(message: Message, folder: String) -> Promise { @@ -21,7 +21,7 @@ extension GmailService: MessageOperationsProvider { } func delete(message: Message, form folderPath: String?) -> Promise { - Promise { (resolve, reject) in + Promise { resolve, reject in guard let identifier = message.identifier.stringId else { return reject(GmailServiceError.missedMessageInfo("id")) } @@ -31,7 +31,7 @@ extension GmailService: MessageOperationsProvider { identifier: identifier ) - self.gmailService.executeQuery(query) { (_, _, error) in + self.gmailService.executeQuery(query) { _, _, error in if let error = error { reject(GmailServiceError.providerError(error)) } @@ -50,7 +50,7 @@ extension GmailService: MessageOperationsProvider { } private func update(message: Message, labelsToAdd: [MessageLabelType] = [], labelsToRemove: [MessageLabelType] = []) -> Promise { - Promise { (resolve, reject) in + Promise { resolve, reject in guard let identifier = message.identifier.stringId else { return reject(GmailServiceError.missedMessageInfo("id")) } @@ -63,7 +63,7 @@ extension GmailService: MessageOperationsProvider { identifier: identifier ) - self.gmailService.executeQuery(query) { (_, _, error) in + self.gmailService.executeQuery(query) { _, _, error in if let error = error { reject(GmailServiceError.providerError(error)) } diff --git a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift index 1c60de7a5..86448a5ce 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift @@ -41,7 +41,7 @@ extension Imap: MessageOperationsProvider { // MARK: - trash func moveMessageToTrash(message: Message, trashPath: String?, from folder: String) -> Promise { - return Promise { [weak self] (resolve, reject) in + return Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { @@ -52,7 +52,7 @@ extension Imap: MessageOperationsProvider { return reject(ImapError.missedMessageInfo("trashPath")) } - try await(self.moveMsg(with: identifier, folder: folder, destFolder: trashPath)) + try awaitPromise(self.moveMsg(with: identifier, folder: folder, destFolder: trashPath)) resolve(()) } } @@ -69,7 +69,7 @@ extension Imap: MessageOperationsProvider { // MARK: - delete func delete(message: Message, form folderPath: String?) -> Promise { - return Promise { [weak self] (_, reject) in + return Promise { [weak self] _, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { @@ -80,8 +80,8 @@ extension Imap: MessageOperationsProvider { return reject(ImapError.missedMessageInfo("folderPath")) } - try await(self.pushUpdatedMsgFlags(with: identifier, folder: folderPath, flags: MCOMessageFlag.deleted)) - try await(self.expungeMsgs(folder: folderPath)) + try awaitPromise(self.pushUpdatedMsgFlags(with: identifier, folder: folderPath, flags: MCOMessageFlag.deleted)) + try awaitPromise(self.expungeMsgs(folder: folderPath)) } } @@ -113,14 +113,14 @@ extension Imap: MessageOperationsProvider { // MARK: - archive func archiveMessage(message: Message, folderPath: String) -> Promise { - return Promise { [weak self] (_, reject) in + return Promise { [weak self] _, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { return reject(ImapError.missedMessageInfo("intId")) } - try await(self.pushUpdatedMsgFlags(with: identifier, folder: folderPath, flags: MCOMessageFlag.deleted)) + try awaitPromise(self.pushUpdatedMsgFlags(with: identifier, folder: folderPath, flags: MCOMessageFlag.deleted)) } } } diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Gmail+MessagesList.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Gmail+MessagesList.swift index f43a46c72..a69b08b59 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Gmail+MessagesList.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Gmail+MessagesList.swift @@ -6,15 +6,15 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Promises -import GTMSessionFetcher -import GoogleAPIClientForREST import FlowCryptCommon +import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: MessagesListProvider { func fetchMessages(using context: FetchMessageContext) -> Promise { - Promise { (resolve, reject) in - let list = try await(fetchMessagesList(using: context)) + Promise { resolve, reject in + let list = try awaitPromise(fetchMessagesList(using: context)) let messageRequests: [Promise] = list.messages?.compactMap(\.identifier).map(fetchFullMessage(with:)) ?? [] all(messageRequests) @@ -50,8 +50,8 @@ extension GmailService { query.q = searchQuery } - return Promise { (resolve, reject) in - self.gmailService.executeQuery(query) { (_, data, error) in + return Promise { resolve, reject in + self.gmailService.executeQuery(query) { _, data, error in if let error = error { reject(GmailServiceError.providerError(error)) return @@ -69,8 +69,8 @@ extension GmailService { private func fetchFullMessage(with identifier: String) -> Promise { let query = GTLRGmailQuery_UsersMessagesGet.query(withUserId: .me, identifier: identifier) query.format = kGTLRGmailFormatFull - return Promise { (resolve, reject) in - self.gmailService.executeQuery(query) { (_, data, error) in + return Promise { resolve, reject in + self.gmailService.executeQuery(query) { _, data, error in if let error = error { reject(GmailServiceError.providerError(error)) return diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift index 16a48e265..001c795fa 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift @@ -21,7 +21,7 @@ extension Imap: MessagesListProvider { return Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } - let folderInfo = try await(self.folderInfo(for: folderPath)) + let folderInfo = try awaitPromise(self.folderInfo(for: folderPath)) let totalCount = Int(folderInfo.messageCount) if totalCount == 0 { resolve(MessageContext(messages: [], pagination: .byNumber(total: totalCount))) @@ -32,7 +32,7 @@ extension Imap: MessagesListProvider { from: from ?? 0 ) let kind = self.messageKindProvider.imapMessagesRequestKind - let messages = try await(self.fetchMsgsByNumber(for: folderPath, kind: kind, set: set)) + let messages = try awaitPromise(self.fetchMsgsByNumber(for: folderPath, kind: kind, set: set)) .map(Message.init) resolve(MessageContext(messages: messages, pagination: .byNumber(total: totalCount))) diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift index 1a19c4e71..b7bc228c5 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Foundation import FlowCryptCommon +import Foundation import GoogleAPIClientForREST struct Message: Hashable { diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift index 199ee8976..aff11ca6e 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageLabel.swift @@ -100,7 +100,7 @@ extension MessageLabelType { init(gmailLabel: String) { let types: [MessageLabelType] = [.seen, .unread, .starred, .sent, .trash, .draft, .important] let all = types.map { type in - return (type, type.value) + (type, type.value) } guard let label = all.first(where: { $0.1 == gmailLabel })?.0 else { self = .label(gmailLabel) diff --git a/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Gmail+Search.swift b/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Gmail+Search.swift index eb03d0be2..b318ade2c 100644 --- a/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Gmail+Search.swift +++ b/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Gmail+Search.swift @@ -6,14 +6,14 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import Promises -import GTMSessionFetcher import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: MessageSearchProvider { func searchExpression(using context: MessageSearchContext) -> Promise<[Message]> { - Promise { (resolve, _) in - let context = try await(self.fetchMessages(using: FetchMessageContext(searchContext: context))) + Promise { resolve, _ in + let context = try awaitPromise(self.fetchMessages(using: FetchMessageContext(searchContext: context))) resolve(context.messages) } } diff --git a/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Imap+Search.swift b/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Imap+Search.swift index bfdc6fdf3..3492769ad 100644 --- a/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Imap+Search.swift +++ b/FlowCrypt/Functionality/Mail Provider/SearchMessage Provider/Imap+Search.swift @@ -27,9 +27,9 @@ extension Imap: MessageSearchProvider { let kind = self.messageKindProvider.imapMessagesRequestKind let path = searchContext.folderPath ?? "INBOX" - let indexes = try await(self.fetchUids(folder: path, expr: expression)) + let indexes = try awaitPromise(self.fetchUids(folder: path, expr: expression)) - let messages = try await( + let messages = try awaitPromise( self.fetchMessagesByUIDOperation( for: path, kind: kind, diff --git a/FlowCrypt/Functionality/Migration/DBMigrationService.swift b/FlowCrypt/Functionality/Migration/DBMigrationService.swift index 25645cf63..506f5a615 100644 --- a/FlowCrypt/Functionality/Migration/DBMigrationService.swift +++ b/FlowCrypt/Functionality/Migration/DBMigrationService.swift @@ -19,6 +19,8 @@ struct DBMigrationService { private let localStorage: LocalStorageType private let encryptedStorage: EncryptedStorageType + private let logger = Logger.nested(in: Self.self, with: .migration) + init(localStorage: LocalStorageType, encryptedStorage: EncryptedStorageType) { self.localStorage = localStorage self.encryptedStorage = encryptedStorage @@ -29,66 +31,15 @@ struct DBMigrationService { extension DBMigrationService: DBMigration { func performMigrationIfNeeded() -> Promise { Promise { - self.performTokenEncryptedMigration() - self.performUserSessionMigration() - self.performGmailApiMigration() - } - } -} - -// MARK: Token -extension DBMigrationService { - /// Perform migration for users which has token saved in non encrypted storage - private func performTokenEncryptedMigration() { - let legacyTokenIndex = "keyCurrentToken" - guard previouslyStoredUser() != nil else { - debugPrint("Local migration not needed. User was not stored in local storage") - return - } - guard let token = localStorage.storage.string(forKey: legacyTokenIndex) else { - debugPrint("Local migration not needed. Token was not saved in local storage") - return - } - - performSessionMigration(with: token) - localStorage.storage.removeObject(forKey: legacyTokenIndex) - } -} - -// MARK: User session -extension DBMigrationService { - /// Perform migration from google signing to generic session - private func performUserSessionMigration() { - guard let token = encryptedStorage.currentToken() else { - debugPrint("User migration not needed. Token was not stored or migration already finished") - return - } - - performSessionMigration(with: token) - } - - private func performSessionMigration(with token: String) { - guard let user = previouslyStoredUser() else { - debugPrint("User migration not needed. User was not stored or migration already finished") - return + // self.performGmailApiMigration() } - debugPrint("Perform user migration for token") - let userObject = UserObject.googleUser(name: user.name, email: user.email, token: token) - - encryptedStorage.saveActiveUser(with: userObject) - UserDefaults.standard.set(nil, forKey: legacyCurrentUserIndex) - } - - var legacyCurrentUserIndex: String { "keyCurrentUser" } - private func previouslyStoredUser() -> User? { - guard let data = UserDefaults.standard.object(forKey: legacyCurrentUserIndex) as? Data else { return nil } - return try? PropertyListDecoder().decode(User.self, from: data) } } -// MARK: Gmail Api +// MARK: - Migration example +// Perform migration when Gmail Api implemented +// remove after some real migration will be implemented extension DBMigrationService { - /// Perform migration when Gmail Api implemented private func performGmailApiMigration() { let key = "KeyGmailApiMigration" let isMigrated = UserDefaults.standard.bool(forKey: key) @@ -102,7 +53,8 @@ extension DBMigrationService { try storage.write { storage.delete(folders) } - } catch let error { + } catch { + logger.logWarning("Can't perform Gmail Api migration \(error)") assertionFailure("Can't perform Gmail Api migration \(error)") } } diff --git a/FlowCrypt/Functionality/Services/AppStartup.swift b/FlowCrypt/Functionality/Services/AppStartup.swift index 50ac36382..cee8e4ce2 100644 --- a/FlowCrypt/Functionality/Services/AppStartup.swift +++ b/FlowCrypt/Functionality/Services/AppStartup.swift @@ -15,7 +15,6 @@ struct AppStartup { } func initializeApp(window: UIWindow, session: SessionType?) { - let start = DispatchTime.now() DispatchQueue.promises = .global() window.rootViewController = BootstrapViewController() window.makeKeyAndVisible() @@ -25,10 +24,8 @@ struct AppStartup { try self.setupSession() }.then(on: .main) { self.chooseView(for: window, session: session) - log("AppStartup", error: nil, res: nil, start: start) }.catch(on: .main) { error in self.showErrorAlert(with: error, on: window, session: session) - log("AppStartup", error: error, res: nil, start: start) } } @@ -37,11 +34,11 @@ struct AppStartup { } private func setupMigrationIfNeeded() throws { - try await(DataService.shared.performMigrationIfNeeded()) + try awaitPromise(DataService.shared.performMigrationIfNeeded()) } private func setupSession() throws { - try await(renewSessionIfValid()) + try awaitPromise(renewSessionIfValid()) } private func renewSessionIfValid() -> Promise { diff --git a/FlowCrypt/Functionality/Services/AttesterApi.swift b/FlowCrypt/Functionality/Services/AttesterApi.swift index 91bf8a02a..46b4eeb85 100644 --- a/FlowCrypt/Functionality/Services/AttesterApi.swift +++ b/FlowCrypt/Functionality/Services/AttesterApi.swift @@ -34,7 +34,7 @@ extension AttesterApi { func lookupEmail(email: String) -> Promise { Promise { [weak self] () -> PubkeySearchResult in guard let url = self?.urlPub(emailOrLongid: email) else { throw AppErr.nilSelf } - let res = try await(URLSession.shared.call(url, tolerateStatus: [404])) + let res = try awaitPromise(URLSession.shared.call(url, tolerateStatus: [404])) if res.status >= 200, res.status <= 299 { return PubkeySearchResult(email: email, armored: res.data) @@ -67,7 +67,7 @@ extension AttesterApi { headers: headers ) return Promise { () -> String in - let res = try await(URLSession.shared.call(request)) + let res = try awaitPromise(URLSession.shared.call(request)) return res.data.toStr() } } @@ -80,7 +80,7 @@ extension AttesterApi { body: pubkey.data() ) return Promise { () -> String in - let res = try await(URLSession.shared.call(request)) + let res = try awaitPromise(URLSession.shared.call(request)) return res.data.toStr() } } @@ -94,7 +94,7 @@ extension AttesterApi { headers: [URLHeader(value: "application/json", httpHeaderField: "Content-Type")] ) return Promise { () -> Void in - _ = try await(URLSession.shared.call(request)) // will throw on non-200 + _ = try awaitPromise(URLSession.shared.call(request)) // will throw on non-200 } } } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 889dd39f2..9ce28e1fe 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -6,8 +6,8 @@ // Copyright © 2020 FlowCrypt Limited. All rights reserved. // -import UIKit import Promises +import UIKit protocol BackupServiceType { /// get all existed backups @@ -35,12 +35,10 @@ struct BackupService { extension BackupService: BackupServiceType { func fetchBackups(for userId: UserId) -> Promise<[KeyDetails]> { Promise<[KeyDetails]> { resolve, reject in - let backupData = try await(self.backupProvider.searchBackups(for: userId.email)) + let backupData = try awaitPromise(self.backupProvider.searchBackups(for: userId.email)) do { let parsed = try self.core.parseKeys(armoredOrBinary: backupData) - // TODO: - TOM 2 - // After parsing keys there are 51 key instead of 17 attachments fetched. let keys = parsed.keyDetails.filter { $0.private != nil } resolve(keys) } catch { @@ -76,7 +74,7 @@ extension BackupService: BackupServiceType { atts: attachments ) let backupEmail = try self.core.composeEmail(msg: message, fmt: .plain, pubKeys: nil) - try await(messageSender.sendMail(mime: backupEmail.mimeEncoded)) + try awaitPromise(messageSender.sendMail(mime: backupEmail.mimeEncoded)) } } @@ -91,7 +89,7 @@ extension BackupService: BackupServiceType { } // MARK: - Helpers -fileprivate extension String { +private extension String { var userReadableEmail: String { self.replacingOccurrences( of: "[^a-z0-9]", @@ -101,7 +99,7 @@ fileprivate extension String { } } -fileprivate extension UserId { +private extension UserId { var toMime: String { "\(name) <\(email)>" } diff --git a/FlowCrypt/Functionality/Services/Contacts Services/ContactsService.swift b/FlowCrypt/Functionality/Services/Contacts Services/ContactsService.swift index b956fc604..f270faeaa 100644 --- a/FlowCrypt/Functionality/Services/Contacts Services/ContactsService.swift +++ b/FlowCrypt/Functionality/Services/Contacts Services/ContactsService.swift @@ -15,7 +15,6 @@ enum ContactsError: Error { } protocol ContactsServiceType: PublicKeyProvider, ContactsProviderType { - } protocol PublicKeyProvider { diff --git a/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift b/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift index 77acb76a0..38d1253a4 100644 --- a/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift +++ b/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift @@ -29,8 +29,8 @@ struct RemoteContactsProvider { extension RemoteContactsProvider: ContactsProviderType { func searchContact(with email: String) -> Promise { Promise { resolve, _ in - let armoredData = try await(self.api.lookupEmail(email: email)).armored - let contact = try await(self.parseKey(data: armoredData, for: email)) + let armoredData = try awaitPromise(self.api.lookupEmail(email: email)).armored + let contact = try awaitPromise(self.parseKey(data: armoredData, for: email)) resolve(contact) } } @@ -65,7 +65,7 @@ extension RemoteContactsProvider: ContactsProviderType { algo: keyDetail.algo ) return Promise(contact) - } catch let error { + } catch { let message = "Armored or binary are not parsed.\n\(error.localizedDescription)" return Promise(ContactsError.unexpected(message)) } diff --git a/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift b/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift index 713e996ff..856426467 100644 --- a/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift +++ b/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift @@ -50,7 +50,7 @@ final class FoldersService: FoldersServiceType { Promise<[FolderViewModel]> { [weak self] resolve, _ in guard let self = self else { throw AppErr.nilSelf } // fetch all folders - let remoteFolders = try await(self.remoteFoldersProvider.fetchFolders()) + let remoteFolders = try awaitPromise(self.remoteFoldersProvider.fetchFolders()) DispatchQueue.main.async { // TODO: - ANTON - instead of removing all folders remove only @@ -73,7 +73,7 @@ final class FoldersService: FoldersServiceType { private func saveTrashFolderPath(with folders: [FolderObject]) { let paths = folders.compactMap { $0.path } guard let path = paths.firstCaseInsensitive("trash") ?? paths.firstCaseInsensitive("deleted") else { - debugPrint("###Warning### Trash folder not found") + Logger.logWarning("Trash folder not found") return } localStorage.saveTrashFolder(path: path) diff --git a/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/GmailService+folders.swift b/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/GmailService+folders.swift index e68ffb331..d91c488ef 100644 --- a/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/GmailService+folders.swift +++ b/FlowCrypt/Functionality/Services/Folders Services/RemoteFoldersProviderType/GmailService+folders.swift @@ -7,16 +7,16 @@ // import Foundation -import Promises -import GTMSessionFetcher import GoogleAPIClientForREST +import GTMSessionFetcher +import Promises extension GmailService: RemoteFoldersProviderType { func fetchFolders() -> Promise<[FolderObject]> { - Promise { (resolve, reject) in + Promise { resolve, reject in let query = GTLRGmailQuery_UsersLabelsList.query(withUserId: .me) - self.gmailService.executeQuery(query) { (_, data, error) in + self.gmailService.executeQuery(query) { _, data, error in if let error = error { reject(GmailServiceError.providerError(error)) return @@ -34,11 +34,11 @@ extension GmailService: RemoteFoldersProviderType { let folders = labels .compactMap { (label) -> GTLRGmail_Label? in guard let identifier = label.identifier, identifier.isNotEmpty else { - debugPrint("[GmailService] skip label with \(label.identifier ?? "")") + logger.logInfo("skip label with \(label.identifier ?? "")") return nil } guard identifier.range(of: "CATEGORY_", options: .caseInsensitive) == nil else { - debugPrint("[GmailService] skip category label with \(label.identifier ?? "")") + logger.logInfo("Skip category label with \(label.identifier ?? "")") return nil } return label diff --git a/FlowCrypt/Functionality/Services/Folders Services/TrashFolderProvider.swift b/FlowCrypt/Functionality/Services/Folders Services/TrashFolderProvider.swift index c372ba08b..4dd6b1ad2 100644 --- a/FlowCrypt/Functionality/Services/Folders Services/TrashFolderProvider.swift +++ b/FlowCrypt/Functionality/Services/Folders Services/TrashFolderProvider.swift @@ -26,9 +26,9 @@ extension TrashFolderProvider: TrashFolderProviderType { if let path = localStorage.trashFolderPath { return Promise(path) } else { - return Promise { (resolve, _) in + return Promise { resolve, _ in // will get all folders - _ = try await(folderProvider.fetchFolders()) + _ = try awaitPromise(folderProvider.fetchFolders()) resolve(localStorage.trashFolderPath) } } diff --git a/FlowCrypt/Functionality/Services/GlobalRouter.swift b/FlowCrypt/Functionality/Services/GlobalRouter.swift index 2bf75b3eb..55d8ff3d5 100644 --- a/FlowCrypt/Functionality/Services/GlobalRouter.swift +++ b/FlowCrypt/Functionality/Services/GlobalRouter.swift @@ -6,8 +6,8 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // -import UIKit import Promises +import UIKit protocol GlobalRouterType { func proceed() @@ -40,6 +40,8 @@ final class GlobalRouter: GlobalRouterType { private let userAccountService: UserAccountServiceType private let googleService: GoogleUserService + private lazy var logger = Logger.nested(in: Self.self, with: .userAppStart) + init( userAccountService: UserAccountServiceType = UserAccountService(), googleService: GoogleUserService = GoogleUserService() @@ -57,6 +59,7 @@ extension GlobalRouter { } private func proceed(with session: SessionType?) { + logger.logInfo("proceed for session \(session.debugDescription)") // make sure it runs on main thread let window = keyWindow DispatchQueue.main.async { @@ -68,6 +71,8 @@ extension GlobalRouter { // MARK: - extension GlobalRouter { func signIn(with rout: GlobalRoutingType) { + logger.logInfo("Sign in with \(rout)") + switch rout { case .gmailLogin(let viewController): googleService.signIn(in: viewController) @@ -83,18 +88,19 @@ extension GlobalRouter { func signOut() { if let session = userAccountService.startActiveSessionForNextUser() { - debugPrint("[GlobalRouter] start session for another email user") + logger.logInfo("Start session for another email user \(session)") proceed(with: session) } else { - debugPrint("[GlobalRouter] sign out") + logger.logInfo("Sign out") userAccountService.cleanup() proceed() } } func switchActive(user: User) { + logger.logInfo("Switching active user \(user)") guard let session = userAccountService.switchActiveSessionFor(user: user) else { - debugPrint("[GlobalRouter] can't switch active user with \(user.email)") + logger.logWarning("Can't switch active user with \(user.email)") return } proceed(with: session) diff --git a/FlowCrypt/Functionality/Services/GoogleUserService.swift b/FlowCrypt/Functionality/Services/GoogleUserService.swift index 8e0c48f2e..9c9c2a737 100644 --- a/FlowCrypt/Functionality/Services/GoogleUserService.swift +++ b/FlowCrypt/Functionality/Services/GoogleUserService.swift @@ -6,11 +6,11 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // +import AppAuth import Foundation +import GTMAppAuth import Promises import RealmSwift -import AppAuth -import GTMAppAuth protocol UserServiceType { func signOut(user email: String) @@ -35,6 +35,7 @@ final class GoogleUserService: NSObject { private enum Constants { static let index = "GTMAppAuthAuthorizerIndex" } + private lazy var logger = Logger.nested(in: Self.self, with: .userAppStart) var userToken: String? { authorization?.authState @@ -57,14 +58,10 @@ extension GoogleUserService: UserServiceType { } func renewSession() -> Promise { - Promise { [weak self] resolve, reject in + // GTMAppAuth should renew session via OIDAuthStateChangeDelegate + Promise { [weak self] resolve, _ in + self?.logger.logInfo("Renew session for google user") resolve(()) -// guard let self = self else { throw AppErr.nilSelf } -// DispatchQueue.main.async { -// self.onNewSession = { resolve(()) } -// self.onError = { error in reject(error) } -// self.googleManager.restorePreviousSignIn() -// } } } @@ -77,7 +74,7 @@ extension GoogleUserService: UserServiceType { let googleAuthSession = OIDAuthState.authState( byPresenting: request, presenting: viewController - ) { (authState, error) in + ) { authState, error in if let authState = authState { let authorization = GTMAppAuthFetcherAuthorization(authState: authState) guard let email = authorization.userEmail else { @@ -168,12 +165,12 @@ extension GoogleUserService { fetcherService.authorizer = authorization fetcherService.fetcher(with: userInfoEndpoint) - .beginFetch { (data, error) in + .beginFetch { data, error in if let data = data { do { let user = try JSONDecoder().decode(GoogleUser.self, from: data) completion(.success(user)) - } catch let error { + } catch { completion(.failure(.parsingError(error))) } } else if let error = error { @@ -186,12 +183,12 @@ extension GoogleUserService { private func handleUserInfo(error: Error) { if (error as NSError).isEqual(OIDOAuthTokenErrorDomain) { - debugPrint("[GoogleUserService] Authorization error during token refresh, clearing state. \(error)") + logger.logError("Authorization error during token refresh, clearing state. \(error)") if let email = currentUserEmail { GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: Constants.index + email) } } else { - debugPrint("[GoogleUserService] Authorization error during fetching user info") + logger.logError("Authorization error during fetching user info") } } } diff --git a/FlowCrypt/Functionality/Services/LoggerService.swift b/FlowCrypt/Functionality/Services/LoggerService.swift deleted file mode 100644 index 8d9c0b094..000000000 --- a/FlowCrypt/Functionality/Services/LoggerService.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// Logger.swift -// FlowCrypt -// -// Created by Anton Kharchevskyi on 8/29/19. -// Copyright © 2019 FlowCrypt Limited. All rights reserved. -// - -import FlowCryptCommon -import Foundation - -func log(_ message: String, error: Error?, res: Any?, start: DispatchTime) { - Logger().log(message, error: error, res: res, start: start) -} - -func logDebug( - _ id: Int? = nil, - _ msg: String? = nil, - value: Any? = nil, - fileName: String = #file, - functionName: String = #function, - lineNumber: Int = #line, - columnNumber: Int = #column -) { - if let message = msg, let id = id { - Logger().debug(id, message, value: value) - } else { - debugPrint("•••• \(fileName) - \(functionName) at line \(lineNumber)[\(columnNumber)]") - } -} - -private struct Logger { - func log(_ message: String, error: Error?, res: Any?, start: DispatchTime) { - let errStr = error.map { "\($0)" } ?? "" - var resStr = "Unknown" - if res == nil { - resStr = "nil" - } else if let data = res as? Data { - resStr = "Data[\(res != nil ? (data.count < 1204 ? "\(data.count)" : "\(data.count / 1024)k") : "-")]" - } else if let res = res as? NSArray { - resStr = "Array[\(res.count)]" - } else if let res = res as? MCOIndexSet { - resStr = "IndexSet[\(res.count())]" - } - debugPrint("IMAP \(message) -> \(errStr) \(resStr) \(start.millisecondsSince)ms") - } - - func debug(_ id: Int, _ msg: String, value: Any? = nil) { // temporary function while we debug token refreshing - debugPrint("[Imap debug \(id) - \(msg)] \(String(describing: value))") - } -} diff --git a/FlowCrypt/Models/Realm Models/Token.swift b/FlowCrypt/Models/Realm Models/Token.swift deleted file mode 100644 index 1995442c6..000000000 --- a/FlowCrypt/Models/Realm Models/Token.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Token.swift -// FlowCrypt -// -// Created by Anton Kharchevskyi on 25.11.2019. -// Copyright © 2019 FlowCrypt Limited. All rights reserved. -// - -import Foundation -import RealmSwift - -// TODO: - ANTON - remove in https://github.com/FlowCrypt/flowcrypt-ios/issues/284 -@available(*, deprecated, message: "Use UserObject instead. Remove in scope of https://github.com/FlowCrypt/flowcrypt-ios/issues/284") -final class EmailAccessToken: Object { - @objc dynamic var value: String = "" - - convenience init(value: String) { - self.init() - self.value = value - } -} diff --git a/FlowCrypt/Models/Realm Models/UserObject.swift b/FlowCrypt/Models/Realm Models/UserObject.swift index 0410e5c66..f4f0c5708 100644 --- a/FlowCrypt/Models/Realm Models/UserObject.swift +++ b/FlowCrypt/Models/Realm Models/UserObject.swift @@ -41,6 +41,10 @@ final class UserObject: Object { override class func primaryKey() -> String? { "email" } + + override var description: String { + email + } } extension UserObject { diff --git a/FlowCryptCommon/Extensions/CollectionExtensions.swift b/FlowCryptCommon/Extensions/CollectionExtensions.swift index 669022218..a7da04acd 100644 --- a/FlowCryptCommon/Extensions/CollectionExtensions.swift +++ b/FlowCryptCommon/Extensions/CollectionExtensions.swift @@ -47,7 +47,7 @@ public extension Array where Element == String { } func containsCaseInsensitive(_ stringToCompare: String) -> Bool { - contains(where: { $0.caseInsensitiveCompare(stringToCompare) == .orderedSame} ) + contains(where: { $0.caseInsensitiveCompare(stringToCompare) == .orderedSame } ) } } diff --git a/FlowCryptCommon/Extensions/Either.swift b/FlowCryptCommon/Extensions/Either.swift index dae2da180..523c78955 100644 --- a/FlowCryptCommon/Extensions/Either.swift +++ b/FlowCryptCommon/Extensions/Either.swift @@ -8,7 +8,7 @@ import Foundation -public enum Either{ +public enum Either { case left(A) case right(B) } diff --git a/FlowCryptCommon/Extensions/IntExtensions.swift b/FlowCryptCommon/Extensions/IntExtensions.swift index dd6853767..c4d997ec8 100644 --- a/FlowCryptCommon/Extensions/IntExtensions.swift +++ b/FlowCryptCommon/Extensions/IntExtensions.swift @@ -13,3 +13,15 @@ public extension Int { Date(timeIntervalSince1970: TimeInterval(self)) } } + +public extension Double { + /// Rounds the double to decimal places value + func rounded(toPlaces places: Int) -> Double { + let divisor = pow(10.0, Double(places)) + return (self * divisor).rounded() / divisor + } + + func roundedString(toPlace: Int) -> String { + String(format: "%.\(toPlace)f", self) + } +} diff --git a/FlowCryptCommon/Extensions/StringExtensions.swift b/FlowCryptCommon/Extensions/StringExtensions.swift index 2ddd6a890..dde16106a 100644 --- a/FlowCryptCommon/Extensions/StringExtensions.swift +++ b/FlowCryptCommon/Extensions/StringExtensions.swift @@ -26,7 +26,7 @@ public extension String { ) -> String { String( self.enumerated() - .map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]} + .map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1] } .joined() ) } diff --git a/FlowCryptCommon/Extensions/UIColorExtensions.swift b/FlowCryptCommon/Extensions/UIColorExtensions.swift index ae56d6df9..4f4e8f12b 100644 --- a/FlowCryptCommon/Extensions/UIColorExtensions.swift +++ b/FlowCryptCommon/Extensions/UIColorExtensions.swift @@ -10,13 +10,9 @@ import UIKit public extension UIColor { static func colorFor(darkStyle: UIColor, lightStyle: UIColor) -> UIColor { - if #available(iOS 13.0, *) { - switch UITraitCollection.current.userInterfaceStyle { - case .dark: return darkStyle - default: return lightStyle - } - } else { - return lightStyle + switch UITraitCollection.current.userInterfaceStyle { + case .dark: return darkStyle + default: return lightStyle } } } diff --git a/FlowCryptCommon/Trace.swift b/FlowCryptCommon/Trace.swift new file mode 100644 index 000000000..d4ffc436b --- /dev/null +++ b/FlowCryptCommon/Trace.swift @@ -0,0 +1,37 @@ +// +// Trace.swift +// FlowCrypt +// +// Created by Anton Kharchevskyi on 02.05.2021. +// Copyright © 2021 FlowCrypt Limited. All rights reserved. +// + +import CoreFoundation +import Foundation + +public final class Trace { + private let id: String + private var startTime: CFAbsoluteTime? + + public init(id: String) { + self.id = id + self.startTime = CFAbsoluteTimeGetCurrent() + } + + public func result() -> TimeInterval { + guard let startTime = startTime else { + return 0 + } + let endTime = CFAbsoluteTimeGetCurrent() + + return (endTime - startTime) + } + + public func finish(roundedTo: Int = 3) -> String { + let resultValue = result() + + let timeValue: String = resultValue <= 1 ? " ms" : " sec" + + return resultValue.roundedString(toPlace: roundedTo) + timeValue + } +} diff --git a/FlowCryptTests/ExtensionTests.swift b/FlowCryptTests/ExtensionTests.swift index 60ebda442..c34d9847b 100644 --- a/FlowCryptTests/ExtensionTests.swift +++ b/FlowCryptTests/ExtensionTests.swift @@ -37,8 +37,8 @@ extension ExtensionTests { let someEmptyCollection: [String] = [] let nonEmptyCollection = [1, 2, 3] - XCTAssert(someEmptyCollection.count == 0) - XCTAssert(nonEmptyCollection.count != 0) + XCTAssert(someEmptyCollection.isEmpty) + XCTAssert(!nonEmptyCollection.isEmpty) } func test_safe_subscript() { diff --git a/FlowCryptTests/FlowCryptCoreTests.swift b/FlowCryptTests/FlowCryptCoreTests.swift index 1c8fe42ca..8478d4c3f 100644 --- a/FlowCryptTests/FlowCryptCoreTests.swift +++ b/FlowCryptTests/FlowCryptCoreTests.swift @@ -153,7 +153,6 @@ class FlowCryptCoreTests: XCTestCase { _ = try core.decryptKey(armoredPrv: "not really a key", passphrase: "whatnot") XCTFail("Should have thrown above") } catch let CoreError.exception(message) { - debugPrint(message) XCTAssertNotNil(message.range(of: "Error: Misformed armored text")) } } diff --git a/FlowCryptUI/Cell Nodes/CheckBoxTextNode.swift b/FlowCryptUI/Cell Nodes/CheckBoxTextNode.swift index fde2887a3..d165155cd 100644 --- a/FlowCryptUI/Cell Nodes/CheckBoxTextNode.swift +++ b/FlowCryptUI/Cell Nodes/CheckBoxTextNode.swift @@ -80,7 +80,7 @@ public final class CheckBoxTextNode: CellNode { children: [checkBox, textStack] ) - return ASInsetLayoutSpec( + return ASInsetLayoutSpec( insets: input.insets, child: stack ) @@ -93,7 +93,7 @@ public final class CheckBoxTextNode: CellNode { children: [checkBox, textNode] ) - return ASInsetLayoutSpec( + return ASInsetLayoutSpec( insets: input.insets, child: stack ) diff --git a/FlowCryptUI/Cell Nodes/ContactDetailNode.swift b/FlowCryptUI/Cell Nodes/ContactDetailNode.swift index d996bd9b7..90974325b 100644 --- a/FlowCryptUI/Cell Nodes/ContactDetailNode.swift +++ b/FlowCryptUI/Cell Nodes/ContactDetailNode.swift @@ -16,14 +16,13 @@ public final class ContactDetailNode: CellNode { let algoInfo: NSAttributedString? let created: NSAttributedString? - public init( user: NSAttributedString, ids: NSAttributedString, fingerprints: NSAttributedString, algoInfo: NSAttributedString?, created: NSAttributedString? - ){ + ) { self.user = user self.ids = ids self.fingerprints = fingerprints diff --git a/FlowCryptUI/Cell Nodes/InfoCellNode.swift b/FlowCryptUI/Cell Nodes/InfoCellNode.swift index 99ac732a0..0956e48f7 100644 --- a/FlowCryptUI/Cell Nodes/InfoCellNode.swift +++ b/FlowCryptUI/Cell Nodes/InfoCellNode.swift @@ -40,7 +40,7 @@ public final class InfoCellNode: CellNode { self.imageNode.image = input?.image self.automaticallyManagesSubnodes = true - if let backgroundColor = input?.backgroundColor{ + if let backgroundColor = input?.backgroundColor { self.backgroundColor = backgroundColor } } diff --git a/FlowCryptUI/Cell Nodes/RecipientEmailNode.swift b/FlowCryptUI/Cell Nodes/RecipientEmailNode.swift index 475a549b0..de84b6597 100644 --- a/FlowCryptUI/Cell Nodes/RecipientEmailNode.swift +++ b/FlowCryptUI/Cell Nodes/RecipientEmailNode.swift @@ -76,7 +76,7 @@ final class RecipientEmailNode: CellNode { imageNode.alpha = 1 let animation = CABasicAnimation(keyPath: "transform.rotation") animation.fromValue = 0 - animation.toValue = Double.pi * 2.0 + animation.toValue = Double.pi * 2.0 animation.duration = 2 animation.repeatCount = .infinity animation.isRemovedOnCompletion = false @@ -89,7 +89,7 @@ final class RecipientEmailNode: CellNode { imageNode.alpha = 1 let animation = CABasicAnimation(keyPath: "transform.scale") animation.fromValue = 0.9 - animation.toValue = 1.0 + animation.toValue = 1.0 animation.duration = 0.5 animation.repeatCount = 1 animation.isRemovedOnCompletion = false diff --git a/FlowCryptUI/Cell Nodes/SwitchCellNode.swift b/FlowCryptUI/Cell Nodes/SwitchCellNode.swift index 119e27d32..7d2ce5646 100644 --- a/FlowCryptUI/Cell Nodes/SwitchCellNode.swift +++ b/FlowCryptUI/Cell Nodes/SwitchCellNode.swift @@ -51,10 +51,9 @@ public final class SwitchCellNode: CellNode { self.textNode.attributedText = input?.attributedText self.automaticallyManagesSubnodes = true - if let backgroundColor = input?.backgroundColor{ + if let backgroundColor = input?.backgroundColor { self.backgroundColor = backgroundColor } - } @objc private func handleAction(_ sender: UISwitch) { diff --git a/FlowCryptUI/Cell Nodes/TextCellNode.swift b/FlowCryptUI/Cell Nodes/TextCellNode.swift index 3a62bc4b5..4291c9b73 100644 --- a/FlowCryptUI/Cell Nodes/TextCellNode.swift +++ b/FlowCryptUI/Cell Nodes/TextCellNode.swift @@ -67,13 +67,9 @@ final class SpinnerNode: ASDisplayNode { override init() { super.init() setViewBlock { - if #available(iOS 13.0, *) { - switch UITraitCollection.current.userInterfaceStyle { - case .dark: return UIActivityIndicatorView(style: .white) - default: return UIActivityIndicatorView(style: .gray) - } - } else { - return UIActivityIndicatorView(style: .gray) + switch UITraitCollection.current.userInterfaceStyle { + case .dark: return UIActivityIndicatorView(style: .white) + default: return UIActivityIndicatorView(style: .gray) } } style.preferredSize = CGSize(width: 20.0, height: 20.0) diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index 9bc57cca2..7af75cc59 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -7,76 +7,18 @@ // import AsyncDisplayKit -import FlowCryptUI -import Promises -public struct Attachment { - var name, size: String - - public init( - name: String, - size: Int - ) { - self.name = name - self.size = ByteCountFormatter.string(fromByteCount: Int64(size), - countStyle: .file) - } -} - -extension Attachment { - init(block: MsgBlock) { - self.name = block.attMeta?.name ?? "/" - self.size = ByteCountFormatter.string(fromByteCount: Int64(block.attMeta?.length ?? 0), - countStyle: .file) - } -} - - -public final class AttachmentsNode: CellNode { +public final class AttachmentNode: CellNode { public struct Input { - let name: String - let size: String + let name: NSAttributedString + let size: NSAttributedString - public init( - name: String, - size: String - ) { + public init(name: NSAttributedString, size: NSAttributedString) { self.name = name self.size = size } } - - private var attachmentNodes: [AttachmentNode] = [] - private var onTap: (() -> Void)? - - public init(attachments: [Attachment], onTap: (() -> Void)?) { - super.init() - self.onTap = onTap - attachmentNodes = attachments.map { - AttachmentNode( - input: AttachmentNode.Input(name: $0.name, size: $0.size), - onTap: { self.onTap?() } - ) - } - } - - public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { - return ASInsetLayoutSpec( - insets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), - child: ASStackLayoutSpec( - direction: .vertical, - spacing: 8, - justifyContent: .start, - alignItems: .stretch, - children: attachmentNodes)) - } -} - -public final class AttachmentNode: CellNode { - public struct Input { - var name, size: String - } - + private let titleNode = ASTextNode() private let subtitleNode = ASTextNode2() private let imageNode = ASImageNode() @@ -84,7 +26,7 @@ public final class AttachmentNode: CellNode { private var onTap: (() -> Void)? - public init(input: Input, onTap: (() -> Void)?) { + init(input: Input, onTap: (() -> Void)?) { super.init() self.onTap = onTap @@ -98,10 +40,10 @@ public final class AttachmentNode: CellNode { imageNode.image = UIImage(named: "paperclip")?.tinted(.gray) buttonNode.setImage(UIImage(named: "download")?.tinted(.gray), for: .normal) buttonNode.addTarget(self, action: #selector(tapHandle), forControlEvents: .touchUpInside) - titleNode.attributedText = NSAttributedString.text(from: input.name, style: .regular(18), color: .gray, alignment: .left) - subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12), color: .gray, alignment: .left) + titleNode.attributedText = input.name + subtitleNode.attributedText = input.size } - + public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { let verticalStack = ASStackLayoutSpec.vertical() verticalStack.spacing = 3 @@ -109,7 +51,7 @@ public final class AttachmentNode: CellNode { verticalStack.style.flexGrow = 1.0 verticalStack.children = [titleNode, subtitleNode] - + let finalSpec = ASStackLayoutSpec( direction: .horizontal, spacing: 10, diff --git a/FlowCryptUI/Nodes/TableNode.swift b/FlowCryptUI/Nodes/TableNode.swift index 0a7f119a2..416df34fa 100644 --- a/FlowCryptUI/Nodes/TableNode.swift +++ b/FlowCryptUI/Nodes/TableNode.swift @@ -29,7 +29,6 @@ public final class TableNode: ASTableNode { withPreviousTraitCollection previousTraitCollection: ASPrimitiveTraitCollection ) { super.asyncTraitCollectionDidChange(withPreviousTraitCollection: previousTraitCollection) - guard #available(iOS 13.0, *) else { return } backgroundColor = .backgroundColor } diff --git a/FlowCryptUI/Nodes/TableViewController.swift b/FlowCryptUI/Nodes/TableViewController.swift index 82f17b8ae..d13e1c863 100644 --- a/FlowCryptUI/Nodes/TableViewController.swift +++ b/FlowCryptUI/Nodes/TableViewController.swift @@ -11,7 +11,6 @@ import AsyncDisplayKit open class TableNodeViewController: ASDKViewController { public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - guard #available(iOS 13.0, *) else { return } node.reloadData() } } diff --git a/FlowCryptUI/Nodes/TextFieldNode.swift b/FlowCryptUI/Nodes/TextFieldNode.swift index 04b044273..75a3ba883 100644 --- a/FlowCryptUI/Nodes/TextFieldNode.swift +++ b/FlowCryptUI/Nodes/TextFieldNode.swift @@ -240,5 +240,3 @@ extension TextFieldNode { onToolbarDoneAction?() } } - - diff --git a/FlowCryptUI/Views/NavigationBarItemsView.swift b/FlowCryptUI/Views/NavigationBarItemsView.swift index 89aed61fa..a9f31110f 100644 --- a/FlowCryptUI/Views/NavigationBarItemsView.swift +++ b/FlowCryptUI/Views/NavigationBarItemsView.swift @@ -52,6 +52,7 @@ public final class NavigationBarItemsView: UIBarButtonItem { } } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/FlowCryptUIApplication/ViewController.swift b/FlowCryptUIApplication/ViewController.swift index 718802512..18c4b9f1b 100644 --- a/FlowCryptUIApplication/ViewController.swift +++ b/FlowCryptUIApplication/ViewController.swift @@ -27,6 +27,7 @@ final class ViewController: TableNodeViewController { super.init(node: TableNode()) } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -83,9 +84,7 @@ extension ViewController: ASTableDelegate, ASTableDataSource { attributedText: title, image: nil ) - let n = InfoCellNode(input: input) - debugPrint(n) - return n + return InfoCellNode(input: input) case .emailRecipients: return RecipientEmailsCellNode(recipients: self.recipients) .onItemSelect { [weak self] indexPath in @@ -240,4 +239,3 @@ extension ViewController { textField?.reset() } } - diff --git a/FlowCryptUITests/SignInGoogleTest.swift b/FlowCryptUITests/SignInGoogleTest.swift index 061118372..f05e1fcb8 100644 --- a/FlowCryptUITests/SignInGoogleTest.swift +++ b/FlowCryptUITests/SignInGoogleTest.swift @@ -78,7 +78,6 @@ class SignInGoogleTest: XCTestCase, AppTest { textField.tap() - textField.typeText(user.email) let returnButton = goKeyboardButton XCTAssert(returnButton.exists, "User keyboard button") @@ -98,7 +97,6 @@ class SignInGoogleTest: XCTestCase, AppTest { XCTAssert(app.tables.firstMatch.exists, "Table does not exist") - // MARK: - Wrong pass phrase // enter wrong pass phrase and tap enter let button = goKeyboardButton @@ -113,7 +111,6 @@ class SignInGoogleTest: XCTestCase, AppTest { wait(0.2) app.tables.secureTextFields.firstMatch.tap() - // MARK: - Correct pass phrase // enter correct pass phrase and tap enter if button.exists { diff --git a/FlowCryptUITests/SignInTest.swift b/FlowCryptUITests/SignInTest.swift index 899cebb06..0284e0b6c 100644 --- a/FlowCryptUITests/SignInTest.swift +++ b/FlowCryptUITests/SignInTest.swift @@ -208,7 +208,7 @@ extension SignInTest { app.tables .staticTexts .allElementsBoundByIndex - .first(where: { $0.label.contains("Sent" )})? + .first(where: { $0.label.contains("Sent" ) })? .tap() wait(3) @@ -282,8 +282,6 @@ extension SignInTest { } } - - /* log in -> approve -> no backups -> generate pubkey -> switch accounts */ diff --git a/Podfile b/Podfile index e87a8380d..dd80a24e6 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,5 @@ # Uncomment the next line to define a global platform for your project -platform :ios, '12.2' +platform :ios, '13.0' use_frameworks! @@ -24,7 +24,7 @@ def shared_pods pod 'SwiftyRSA' pod 'IDZSwiftCommonCrypto' pod 'mailcore2-ios' - pod 'BigInt', '~> 5.0' + pod 'BigInt', '~> 5.2' end def ui_pods @@ -61,10 +61,10 @@ end ## Set IPHONEOS_DEPLOYMENT_TARGET for all pods -post_install do |pi| - pi.pods_project.targets.each do |t| - t.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' - end +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' end + end end diff --git a/Podfile.lock b/Podfile.lock index 30b7ee549..7134a26bf 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -41,14 +41,14 @@ PODS: - PINRemoteImage/PINCache (3.0.1): - PINCache (~> 3.0.1) - PINRemoteImage/Core - - PromisesObjC (1.2.8) - - PromisesSwift (1.2.8): - - PromisesObjC (= 1.2.8) - - Realm (10.7.2): - - Realm/Headers (= 10.7.2) - - Realm/Headers (10.7.2) - - RealmSwift (10.7.2): - - Realm (= 10.7.2) + - PromisesObjC (2.0.0) + - PromisesSwift (2.0.0): + - PromisesObjC (= 2.0.0) + - Realm (10.7.4): + - Realm/Headers (= 10.7.4) + - Realm/Headers (10.7.4) + - RealmSwift (10.7.4): + - Realm (= 10.7.4) - SwiftFormat/CLI (0.44.10) - SwiftLint (0.36.0) - SwiftyRSA (1.5.0): @@ -77,7 +77,7 @@ PODS: - Toast (4.0.0) DEPENDENCIES: - - BigInt (~> 5.0) + - BigInt (~> 5.2) - ENSwiftSideMenu (~> 0.1.4) - GoogleAPIClientForREST/Gmail - GoogleSignIn @@ -132,16 +132,16 @@ SPEC CHECKSUMS: PINCache: bea7c404c360f6fe0f6ee783c5cd14e778fa2aab PINOperation: 3a967a927e7867e61976c6cc23e5770416449fbc PINRemoteImage: 3b7cedb118c2d357f87e9eabc7c81ba0202cb236 - PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6 - PromisesSwift: 37bad6f4daddb02f7c9c531efe91e8b21c13ee2f - Realm: e523da9ade306c5ae87e85dc09fdef148d3e1cc1 - RealmSwift: 4f6758c3adbdcc87f7b7777107226532a077f61c + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + PromisesSwift: e0b2a6433469efb0b83a2b84c62a2abab8e5e5d4 + Realm: f08ce1a85649a41303ecfe1cbd914c62e9e6d710 + RealmSwift: 60ec49d165f23d040ab48811d8dd177e09baf4d3 SwiftFormat: b72e592ea0979aeee53f6052abff291181364933 SwiftLint: fc9859e4e1752340664851f667bb1898b9c90114 SwiftyRSA: 6e528fb1b1a87d5111c48081083b141d6d65f1cf Texture: 2f109e937850d94d1d07232041c9c7313ccddb81 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 -PODFILE CHECKSUM: 6f91555731cbd62d8d40d7455a29c2e1e5ab453d +PODFILE CHECKSUM: 7b986642d7194ebd77a029368e3a1935dc0675bc COCOAPODS: 1.10.1 diff --git a/Scripts/format.sh b/Scripts/format.sh index 7c5f4e65e..e69de29bb 100755 --- a/Scripts/format.sh +++ b/Scripts/format.sh @@ -1,21 +0,0 @@ -#!/bin/bash - -# Do not run format on CI -if "$CI"; then - exit 0 -fi - -# Do not run format if swiftlint isn't installed -if which swiftformat >/dev/null; then - echo "Start formating" - swiftlint autocorrect --path . - swiftformat . \ - --rules trailingSpace \ - --swiftversion 5 -else - echo "warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat" - brew install swiftformat - exit 0 -fi - -################### RULES https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md diff --git a/fastlane/SnapshotHelper.swift b/fastlane/SnapshotHelper.swift index aaa2a9a92..cd94bc1f9 100644 --- a/fastlane/SnapshotHelper.swift +++ b/fastlane/SnapshotHelper.swift @@ -80,7 +80,7 @@ open class Snapshot: NSObject { setLanguage(app) setLocale(app) setLaunchArguments(app) - } catch let error { + } catch { NSLog(error.localizedDescription) } } @@ -184,7 +184,7 @@ open class Snapshot: NSObject { let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") try screenshot.pngRepresentation.write(to: path) - } catch let error { + } catch { NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png") NSLog(error.localizedDescription) } @@ -266,7 +266,7 @@ private extension XCUIElementAttributes { private extension XCUIElementQuery { var networkLoadingIndicators: XCUIElementQuery { - let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in + let isNetworkLoadingIndicator = NSPredicate { evaluatedObject, _ in guard let element = evaluatedObject as? XCUIElementAttributes else { return false } return element.isNetworkLoadingIndicator @@ -282,7 +282,7 @@ private extension XCUIElementQuery { let deviceWidth = app.windows.firstMatch.frame.width - let isStatusBar = NSPredicate { (evaluatedObject, _) in + let isStatusBar = NSPredicate { evaluatedObject, _ in guard let element = evaluatedObject as? XCUIElementAttributes else { return false } return element.isStatusBar(deviceWidth) From 9632392b97ddd577aa5838c2f24ec75ad4b046e1 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Thu, 13 May 2021 11:00:56 +0300 Subject: [PATCH 7/8] Specify autoformatting path and swiftversion --- .swift-version | 1 + .../View Controllers/SegmentedViewController.swift | 4 ++-- FlowCrypt/Controllers/Compose/ComposeViewController.swift | 8 ++++---- .../Enter Pass Phrase/EnterPassPhraseViewController.swift | 4 ++-- .../ImportKey/Import Key/ImportKeyViewController.swift | 4 ++-- FlowCrypt/Controllers/Inbox/InboxViewController.swift | 6 +++--- FlowCrypt/Controllers/Msg/MessageViewController.swift | 4 ++-- .../BackupOptionsViewController.swift | 2 +- .../BackupSelectKeyViewController.swift | 6 +++--- .../Contacts List/ContactDetailViewController.swift | 2 +- .../Contacts List/ContactsListViewController.swift | 2 +- .../Key Detail Info/KeyDetailInfoViewController.swift | 2 +- .../KeySettings/Key Details/KeyDetailViewController.swift | 2 +- .../KeySettings/Key List/KeySettingsViewController.swift | 2 +- .../Public Key/PublicKeyDetailViewController.swift | 2 +- .../Settings/Settings List/SettingsViewController.swift | 2 +- FlowCrypt/Controllers/Setup/SetupViewController.swift | 4 ++-- .../SideMenu/Main/SideMenuNavigationController.swift | 6 +++--- .../Controllers/SideMenu/Menu/MyMenuViewController.swift | 4 ++-- FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift | 2 +- FlowCrypt/Core/Core.swift | 4 ++-- FlowCrypt/Core/CoreHost.swift | 2 +- FlowCrypt/Core/Models/KeyDetails.swift | 2 +- FlowCrypt/Extensions/UIViewControllerExtensions.swift | 6 +++--- FlowCrypt/Extensions/URLSessionExtension.swift | 4 ++-- .../DataManager/Encrypted Storage/EncryptedStorage.swift | 2 +- .../DataManager/Local Storage/LocalStorage.swift | 2 +- .../Mail Provider/Backup Provider/Imap+Backup.swift | 4 ++-- .../Functionality/Mail Provider/Imap/Imap+retry.swift | 2 +- .../Mail Provider/Imap/MessageKindProviderType.swift | 4 ++-- .../Mail Provider/Message Provider/Gmail+Message.swift | 2 +- .../Imap+MessageOperations.swift | 8 ++++---- .../MessagesList Provider/Imap+MessagesList.swift | 2 +- .../MessagesList Provider/Model/Message.swift | 2 +- .../Services/Backup Services/BackupService.swift | 2 +- .../Services/Contacts Services/Models/Contact.swift | 2 +- .../Contacts Services/RemoteContactsProvider.swift | 6 +++--- .../Services/Folders Services/FoldersService.swift | 2 +- FlowCrypt/Functionality/Services/GoogleUserService.swift | 2 +- Scripts/format.sh | 7 +++---- 40 files changed, 68 insertions(+), 68 deletions(-) create mode 100644 .swift-version diff --git a/.swift-version b/.swift-version new file mode 100644 index 000000000..37c2d9960 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +5.4 diff --git a/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift b/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift index 5a9f24ae5..2f6378cbc 100644 --- a/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift +++ b/FlowCrypt/Common UI/View Controllers/SegmentedViewController.swift @@ -87,7 +87,7 @@ final class SegmentedViewController: ASDKViewController { super.viewDidLayoutSubviews() let calculatedWidth = dataSource - .map { $0.title } + .map(\.title) .map { $0.size().width } .reduce(0, +) let collectionWidth = calculatedWidth @@ -126,7 +126,7 @@ extension SegmentedViewController: ASCollectionDataSource, ASCollectionDelegate } func collectionNode(_: ASCollectionNode, nodeBlockForItemAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let item = self.dataSource[safe: indexPath.row] else { return ASCellNode() } return SetupTitleNode( diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 1474b7b3f..300b06ad9 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -256,7 +256,7 @@ extension ComposeViewController { pubkeys: allRecipientPubs + [myPubKey], subject: subject, message: text, - to: recipients.map { $0.email } + to: recipients.map(\.email) ) try awaitPromise(self.messageSender.sendMail(mime: encrypted.mimeEncoded)) @@ -270,14 +270,14 @@ extension ComposeViewController { ($0.email, contactsService.retrievePubKey(for: $0.email)) } - let emailsWithoutPubKeys = pubKeys.filter { $0.1 == nil }.map { $0.0 } + let emailsWithoutPubKeys = pubKeys.filter { $0.1 == nil }.map(\.0) guard emailsWithoutPubKeys.isEmpty else { showNoPubKeyAlert(for: emailsWithoutPubKeys) return nil } - return pubKeys.compactMap { $0.1 } + return pubKeys.compactMap(\.1) } private func showNoPubKeyAlert(for emails: [String]) { @@ -323,7 +323,7 @@ extension ComposeViewController { } private func isInputValid() -> Bool { - let emails = recipients.map { $0.email } + let emails = recipients.map(\.email) let hasContent = emails.filter { $0.hasContent } guard emails.count == hasContent.count else { diff --git a/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift b/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift index a00edbd9d..55d4a5fff 100644 --- a/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift +++ b/FlowCrypt/Controllers/ImportKey/Enter Pass Phrase/EnterPassPhraseViewController.swift @@ -115,11 +115,11 @@ extension EnterPassPhraseViewController { extension EnterPassPhraseViewController: ASTableDelegate, ASTableDataSource { func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int { - return Parts.allCases.count + Parts.allCases.count } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { case .title: diff --git a/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift b/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift index 43d90c63c..f69f7fe67 100644 --- a/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift +++ b/FlowCrypt/Controllers/ImportKey/Import Key/ImportKeyViewController.swift @@ -79,11 +79,11 @@ final class ImportKeyViewController: TableNodeViewController { extension ImportKeyViewController: ASTableDelegate, ASTableDataSource { func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int { - return Parts.allCases.count + Parts.allCases.count } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { case .title: diff --git a/FlowCrypt/Controllers/Inbox/InboxViewController.swift b/FlowCrypt/Controllers/Inbox/InboxViewController.swift index d6d56067c..82c35a15f 100644 --- a/FlowCrypt/Controllers/Inbox/InboxViewController.swift +++ b/FlowCrypt/Controllers/Inbox/InboxViewController.swift @@ -270,7 +270,7 @@ extension InboxViewController { // MARK: - MsgListViewConroller extension InboxViewController: MsgListViewConroller { func msgListGetIndex(message: Message) -> Int? { - return messages.firstIndex(of: message) + messages.firstIndex(of: message) } func msgListRenderAsRemoved(message _: Message, at index: Int) { @@ -342,7 +342,7 @@ extension InboxViewController: ASTableDataSource, ASTableDelegate { // MARK: - Cell Nodes extension InboxViewController { private func cellNode(for indexPath: IndexPath, and size: CGSize) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self else { return ASCellNode() } switch self.state { @@ -433,7 +433,7 @@ extension InboxViewController { // MARK: - Pagination helpers extension InboxViewController { private func currentMessagesListPagination(from number: Int? = nil) -> MessagesListPagination { - return MailProvider.shared.currentMessagesListPagination(from: number, token: state.token) + MailProvider.shared.currentMessagesListPagination(from: number, token: state.token) } private func messagesToLoad() -> Int { diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index 479d649be..da0a913e4 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -290,7 +290,7 @@ extension MessageViewController { } private func awaitUserConfirmation(title: String) -> Promise { - return Promise(on: .main) { [weak self] resolve, _ in + Promise(on: .main) { [weak self] resolve, _ in guard let self = self else { throw AppErr.nilSelf } let alert = UIAlertController(title: "Are you sure?", message: title, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { _ in resolve(false) })) @@ -363,7 +363,7 @@ extension MessageViewController: NavigationChildController { extension MessageViewController: ASTableDelegate, ASTableDataSource { func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int { - return Parts.allCases.count + Parts.allCases.count } func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index fb9a5a643..2a0d64d3f 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -125,7 +125,7 @@ extension BackupOptionsViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index b3c4fe98b..ad5eef482 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -83,7 +83,7 @@ extension BackupSelectKeyViewController { showSpinner() let backupsToSave = backupsContext .filter { $0.1 == true } - .map { $0.0 } + .map(\.0) backupService.backupToInbox(keys: backupsToSave, for: userId) .then(on: .main) { [weak self] in @@ -96,7 +96,7 @@ extension BackupSelectKeyViewController { } private func backupAsFile() { - backupService.backupAsFile(keys: backupsContext.map { $0.0 }, for: self) + backupService.backupAsFile(keys: backupsContext.map(\.0), for: self) } } @@ -107,7 +107,7 @@ extension BackupSelectKeyViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self else { return ASCellNode() } return CheckBoxTextNode( diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift index b9a0b6f89..d0e2233d9 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactDetailViewController.swift @@ -83,7 +83,7 @@ extension ContactDetailViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self else { return ASCellNode() } return ContactDetailNode(input: self.decorator.nodeInput(with: self.contact)) } diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift index 5eccbf0c6..9729a38aa 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListViewController.swift @@ -54,7 +54,7 @@ extension ContactsListViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self else { return ASCellNode() } return ContactCellNode( input: self.decorator.contactNodeInput(with: self.contacts[indexPath.row]), diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift index db5a8fce9..c0c714b9f 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key Detail Info/KeyDetailInfoViewController.swift @@ -56,7 +56,7 @@ extension KeyDetailInfoViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row), let keyId = self.key.ids[safe: indexPath.section] diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift index 6a171c61d..414a7135b 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key Details/KeyDetailViewController.swift @@ -50,7 +50,7 @@ extension KeyDetailViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift index 6be74e2e3..872d26de1 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Key List/KeySettingsViewController.swift @@ -72,7 +72,7 @@ extension KeySettingsViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let key = self.keys[safe: indexPath.row] else { return ASCellNode() } diff --git a/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift b/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift index 4a90ba330..27a25ed07 100644 --- a/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift +++ b/FlowCrypt/Controllers/Settings/KeySettings/Public Key/PublicKeyDetailViewController.swift @@ -38,7 +38,7 @@ extension PublicKeyDetailViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt _: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in SetupTitleNode( SetupTitleNode.Input( title: (self?.text ?? "").attributed(.regular(16)), diff --git a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift index 6b31bbeec..8ef99d8e3 100644 --- a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift @@ -69,7 +69,7 @@ extension SettingsViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let setting = Settings(rawValue: indexPath.row) else { return ASCellNode() } return SettingsCellNode( diff --git a/FlowCrypt/Controllers/Setup/SetupViewController.swift b/FlowCrypt/Controllers/Setup/SetupViewController.swift index 9aa5dda35..d17896b94 100644 --- a/FlowCrypt/Controllers/Setup/SetupViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupViewController.swift @@ -347,7 +347,7 @@ extension SetupViewController { } private func validateAndConfirmNewPassPhraseOrReject(passPhrase: String) -> Promise { - return Promise { + Promise { let strength = try self.core.zxcvbnStrengthBar(passPhrase: passPhrase) guard strength.word.pass else { throw AppErr.user("Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase.") } let confirmPassPhrase = try awaitPromise(self.awaitUserPassPhraseEntry(title: "Confirm Pass Phrase")) @@ -422,7 +422,7 @@ extension SetupViewController: ASTableDelegate, ASTableDataSource { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let part = Parts(rawValue: indexPath.row) else { return ASCellNode() } switch part { case .title: diff --git a/FlowCrypt/Controllers/SideMenu/Main/SideMenuNavigationController.swift b/FlowCrypt/Controllers/SideMenu/Main/SideMenuNavigationController.swift index 9b3960895..581efe7b2 100644 --- a/FlowCrypt/Controllers/SideMenu/Main/SideMenuNavigationController.swift +++ b/FlowCrypt/Controllers/SideMenu/Main/SideMenuNavigationController.swift @@ -22,15 +22,15 @@ final class SideMenuNavigationController: ENSideMenuNavigationController { } override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent + .lightContent } override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { - return .slide + .slide } override var prefersStatusBarHidden: Bool { - return isStatusBarHidden + isStatusBarHidden } private enum Constants { diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift index 4ffb216cf..ed6cfe440 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift @@ -121,7 +121,7 @@ extension MyMenuViewController: ASTableDataSource, ASTableDelegate { } func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let section = Sections(rawValue: indexPath.section) else { return ASCellNode() } @@ -153,7 +153,7 @@ extension MyMenuViewController: ASTableDataSource, ASTableDelegate { } func tableView(_: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return section == Sections.main.rawValue ? 1 : 0 + section == Sections.main.rawValue ? 1 : 0 } } diff --git a/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift b/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift index 84d852006..99a487481 100644 --- a/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift +++ b/FlowCrypt/Controllers/SignIn/SignInViewDecorator.swift @@ -49,7 +49,7 @@ extension SigninButtonNode.Input { enum UserSignInType { case gmail, outlook, other - fileprivate var title: String { + private var title: String { switch self { case .gmail: return "sign_in_gmail".localized case .outlook: return "sign_in_outlook".localized diff --git a/FlowCrypt/Core/Core.swift b/FlowCrypt/Core/Core.swift index ec4bf230d..a35900aba 100644 --- a/FlowCrypt/Core/Core.swift +++ b/FlowCrypt/Core/Core.swift @@ -142,11 +142,11 @@ final class Core { // private private func call(_ endpoint: String, jsonDict: [String: Any?]?, data: Data?) throws -> RawRes { - return try call(endpoint, jsonData: try JSONSerialization.data(withJSONObject: jsonDict ?? [String: String]()), data: data ?? Data()) + try call(endpoint, jsonData: try JSONSerialization.data(withJSONObject: jsonDict ?? [String: String]()), data: data ?? Data()) } private func call(_ endpoint: String, jsonEncodable: Encodable, data: Data) throws -> RawRes { - return try call(endpoint, jsonData: try jsonEncodable.toJsonData(), data: data) + try call(endpoint, jsonData: try jsonEncodable.toJsonData(), data: data) } private func call(_ endpoint: String, jsonData: Data, data: Data) throws -> RawRes { diff --git a/FlowCrypt/Core/CoreHost.swift b/FlowCrypt/Core/CoreHost.swift index f6d435bff..c93bbb8f6 100644 --- a/FlowCrypt/Core/CoreHost.swift +++ b/FlowCrypt/Core/CoreHost.swift @@ -45,7 +45,7 @@ final class CoreHost: NSObject, CoreHostExports { // aes256 msglen:1300, original 11ms, now 7ms // performance untested for larger messages func decryptAesCfbNoPadding(_ ct: [UInt8], _ key: [UInt8], _ iv: [UInt8]) -> [UInt8] { - return Cryptor(operation: .decrypt, algorithm: .aes, mode: .CFB, padding: .NoPadding, key: key, iv: iv).update(byteArray: ct)!.final()! + Cryptor(operation: .decrypt, algorithm: .aes, mode: .CFB, padding: .NoPadding, key: key, iv: iv).update(byteArray: ct)!.final()! } // rsa verify is used by OpenPGP.js during decryption as well to figure out our own key preferences diff --git a/FlowCrypt/Core/Models/KeyDetails.swift b/FlowCrypt/Core/Models/KeyDetails.swift index c98d20021..5fa653af7 100644 --- a/FlowCrypt/Core/Models/KeyDetails.swift +++ b/FlowCrypt/Core/Models/KeyDetails.swift @@ -35,6 +35,6 @@ extension KeyDetails: Equatable { extension Array where Element == KeyDetails { // concatenated private keys, joined with a newline var joinedPrivateKey: String { - compactMap { $0.private }.joined(separator: "\n") + compactMap(\.private).joined(separator: "\n") } } diff --git a/FlowCrypt/Extensions/UIViewControllerExtensions.swift b/FlowCrypt/Extensions/UIViewControllerExtensions.swift index 7f314850f..bc9c953a4 100644 --- a/FlowCrypt/Extensions/UIViewControllerExtensions.swift +++ b/FlowCrypt/Extensions/UIViewControllerExtensions.swift @@ -66,11 +66,11 @@ extension UIViewController { extension UIViewController { var safeAreaWindowInsets: UIEdgeInsets { - return UIApplication.shared.keyWindow?.safeAreaInsets ?? .zero + UIApplication.shared.keyWindow?.safeAreaInsets ?? .zero } var statusBarHeight: CGFloat { - return UIApplication.shared.statusBarFrame.height + UIApplication.shared.statusBarFrame.height } private func errorToUserFriendlyString(error: Error, title: String) -> String? { @@ -128,7 +128,7 @@ extension UIViewController { } func alertAndSkipOnRejection(_ promise: Promise, fail msg: String) -> Promise { - return Promise { [weak self] resolve, _ in + Promise { [weak self] resolve, _ in guard let self = self else { throw AppErr.nilSelf } do { _ = try awaitPromise(promise) diff --git a/FlowCrypt/Extensions/URLSessionExtension.swift b/FlowCrypt/Extensions/URLSessionExtension.swift index 38d7475d9..4ab1fb449 100644 --- a/FlowCrypt/Extensions/URLSessionExtension.swift +++ b/FlowCrypt/Extensions/URLSessionExtension.swift @@ -21,7 +21,7 @@ private let logger = Logger.nested("URLSession") extension URLSession { func call(_ urlRequest: URLRequest, tolerateStatus: [Int]? = nil) -> Promise { - return Promise { resolve, reject in + Promise { resolve, reject in let trace = Trace(id: "call") self.dataTask(with: urlRequest) { data, response, error in let res = response as? HTTPURLResponse @@ -45,7 +45,7 @@ extension URLSession { } func call(_ urlStr: String, tolerateStatus: [Int]? = nil) -> Promise { - return Promise { () -> HttpRes in + Promise { () -> HttpRes in let url = URL(string: urlStr) guard url != nil else { throw HttpErr(status: -2, data: Data(), error: AppErr.unexpected("Invalid url: \(urlStr)")) diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift index 47ddcf5fb..2941ef728 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift @@ -186,7 +186,7 @@ extension EncryptedStorage { func publicKey() -> String? { storage.objects(KeyInfo.self) - .map { $0.public } + .map(\.public) .first } } diff --git a/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift b/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift index 228b749e9..0791f660d 100644 --- a/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Local Storage/LocalStorage.swift @@ -47,7 +47,7 @@ extension LocalStorage: LogOutHandler { func cleanup() { Constants.allCases - .compactMap { $0.rawValue } + .map(\.rawValue) .forEach { storage.removeObject(forKey: $0) } diff --git a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift index 8f0727620..5c2865042 100644 --- a/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift +++ b/FlowCrypt/Functionality/Mail Provider/Backup Provider/Imap+Backup.swift @@ -22,10 +22,10 @@ enum BackupError: Error { extension Imap: BackupProvider { func searchBackups(for email: String) -> Promise { - return Promise { [weak self] () -> Data in + Promise { [weak self] () -> Data in guard let self = self else { throw AppErr.nilSelf } var folderPaths = try awaitPromise(self.fetchFolders()) - .compactMap { $0.path } + .map(\.path) guard folderPaths.isNotEmpty else { throw BackupError.missedFolders diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift index 7abc4e2ad..0608cfb3c 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/Imap+retry.swift @@ -17,7 +17,7 @@ extension Imap { retry: @escaping () -> Promise, start: DispatchTime = DispatchTime.now() ) -> (Error?, T?) -> Void { - return { [weak self] error, res in + { [weak self] error, res in self?.logger.logError("Error \(String(describing: error))") // log(op, error: error, res: res, start: start) guard self?.notRetrying(op, error, resolve, reject, retry: retry) ?? false else { return } diff --git a/FlowCrypt/Functionality/Mail Provider/Imap/MessageKindProviderType.swift b/FlowCrypt/Functionality/Mail Provider/Imap/MessageKindProviderType.swift index 7b6a7e1df..a6b63c43f 100644 --- a/FlowCrypt/Functionality/Mail Provider/Imap/MessageKindProviderType.swift +++ b/FlowCrypt/Functionality/Mail Provider/Imap/MessageKindProviderType.swift @@ -15,7 +15,7 @@ protocol MessageKindProviderType { struct MessageKindProvider: MessageKindProviderType { var messagesRequestKind: Int { - return MCOIMAPMessagesRequestKind.headers.rawValue + MCOIMAPMessagesRequestKind.headers.rawValue | MCOIMAPMessagesRequestKind.structure.rawValue | MCOIMAPMessagesRequestKind.internalDate.rawValue | MCOIMAPMessagesRequestKind.headerSubject.rawValue @@ -24,6 +24,6 @@ struct MessageKindProvider: MessageKindProviderType { } var imapMessagesRequestKind: MCOIMAPMessagesRequestKind { - return MCOIMAPMessagesRequestKind(rawValue: messagesRequestKind) + MCOIMAPMessagesRequestKind(rawValue: messagesRequestKind) } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift index fc68f966f..7a4f67a80 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/Gmail+Message.swift @@ -12,7 +12,7 @@ import Promises extension GmailService: MessageProvider { func fetchMsg(message: Message, folder: String) -> Promise { - return Promise { resolve, reject in + Promise { resolve, reject in guard let identifier = message.identifier.stringId else { return reject(GmailServiceError.missedMessageInfo("id")) } diff --git a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift index 86448a5ce..41cd3e86a 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessageOperations Provider/Imap+MessageOperations.swift @@ -41,7 +41,7 @@ extension Imap: MessageOperationsProvider { // MARK: - trash func moveMessageToTrash(message: Message, trashPath: String?, from folder: String) -> Promise { - return Promise { [weak self] resolve, reject in + Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { @@ -69,7 +69,7 @@ extension Imap: MessageOperationsProvider { // MARK: - delete func delete(message: Message, form folderPath: String?) -> Promise { - return Promise { [weak self] _, reject in + Promise { [weak self] _, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { @@ -102,7 +102,7 @@ extension Imap: MessageOperationsProvider { } private func expungeMsgs(folder: String) -> Promise { - return Promise { [weak self] resolve, reject in + Promise { [weak self] resolve, reject in guard let self = self else { throw AppErr.nilSelf } self.imapSess? @@ -113,7 +113,7 @@ extension Imap: MessageOperationsProvider { // MARK: - archive func archiveMessage(message: Message, folderPath: String) -> Promise { - return Promise { [weak self] _, reject in + Promise { [weak self] _, reject in guard let self = self else { return reject(AppErr.nilSelf) } guard let identifier = message.identifier.intId else { diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift index 001c795fa..0f3217b20 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Imap+MessagesList.swift @@ -40,7 +40,7 @@ extension Imap: MessagesListProvider { } private func folderInfo(for path: String) -> Promise { - return Promise { [weak self] resolve, reject in + Promise { [weak self] resolve, reject in guard let self = self else { return reject(AppErr.nilSelf) } self.imapSess? diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift index b7bc228c5..3afa36de3 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/Message.swift @@ -53,7 +53,7 @@ struct Message: Hashable { extension Message: Equatable { static func == (lhs: Message, rhs: Message) -> Bool { - return lhs.identifier == rhs.identifier + lhs.identifier == rhs.identifier } } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 9ce28e1fe..2d789521a 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -49,7 +49,7 @@ extension BackupService: BackupServiceType { func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise { Promise { () -> Void in - let isFullyEncryptedKeys = keys.map { $0.isFullyDecrypted }.contains(false) + let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) guard isFullyEncryptedKeys else { throw BackupServiceError.keyIsNotFullyEncrypted diff --git a/FlowCrypt/Functionality/Services/Contacts Services/Models/Contact.swift b/FlowCrypt/Functionality/Services/Contacts Services/Models/Contact.swift index 69642c7de..1ed03524d 100644 --- a/FlowCrypt/Functionality/Services/Contacts Services/Models/Contact.swift +++ b/FlowCrypt/Functionality/Services/Contacts Services/Models/Contact.swift @@ -47,7 +47,7 @@ extension Contact { self.pubkeyLastChecked = contactObject.pubkeyLastChecked self.pubkeyExpiresOn = contactObject.pubkeyExpiresOn self.lastUsed = contactObject.lastUsed - self.longids = contactObject.longids.map { $0.value } + self.longids = contactObject.longids.map(\.value) self.fingerprints = contactObject.fingerprints.split(separator: ",").map(String.init) self.algo = contactObject.keyAlgo.flatMap(KeyAlgo.init) self.pubkeyCreated = contactObject.pubkeyCreated diff --git a/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift b/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift index 38d1253a4..4da784da6 100644 --- a/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift +++ b/FlowCrypt/Functionality/Services/Contacts Services/RemoteContactsProvider.swift @@ -47,9 +47,9 @@ extension RemoteContactsProvider: ContactsProviderType { return Promise(ContactsError.unexpected("Key details are not parsed")) } - let keyIds = parsedKey.keyDetails.flatMap { $0.ids } - let longids = keyIds.map { $0.longid } - let fingerprints = keyIds.map { $0.fingerprint } + let keyIds = parsedKey.keyDetails.flatMap(\.ids) + let longids = keyIds.map(\.longid) + let fingerprints = keyIds.map(\.fingerprint) let contact = Contact( email: email, diff --git a/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift b/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift index 856426467..d58c32b5a 100644 --- a/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift +++ b/FlowCrypt/Functionality/Services/Folders Services/FoldersService.swift @@ -71,7 +71,7 @@ final class FoldersService: FoldersServiceType { } private func saveTrashFolderPath(with folders: [FolderObject]) { - let paths = folders.compactMap { $0.path } + let paths = folders.map(\.path) guard let path = paths.firstCaseInsensitive("trash") ?? paths.firstCaseInsensitive("deleted") else { Logger.logWarning("Trash folder not found") return diff --git a/FlowCrypt/Functionality/Services/GoogleUserService.swift b/FlowCrypt/Functionality/Services/GoogleUserService.swift index 9c9c2a737..c1d4f2580 100644 --- a/FlowCrypt/Functionality/Services/GoogleUserService.swift +++ b/FlowCrypt/Functionality/Services/GoogleUserService.swift @@ -122,7 +122,7 @@ extension GoogleUserService { OIDAuthorizationRequest( configuration: GTMAppAuthFetcherAuthorization.configurationForGoogle(), clientId: GeneralConstants.Gmail.clientID, - scopes: GeneralConstants.Gmail.currentScope.map { $0.value } + [OIDScopeEmail], + scopes: GeneralConstants.Gmail.currentScope.map(\.value) + [OIDScopeEmail], redirectURL: GeneralConstants.Gmail.redirectURL, responseType: OIDResponseTypeCode, additionalParameters: nil diff --git a/Scripts/format.sh b/Scripts/format.sh index 6ce9ce41f..44475d2f5 100755 --- a/Scripts/format.sh +++ b/Scripts/format.sh @@ -8,15 +8,14 @@ fi # Do not run format if swiftlint isn't installed if which swiftformat >/dev/null; then echo "Start formatting" - swiftlint autocorrect --path . - swiftformat . \ + # swiftlint autocorrect --path . + swiftformat "FlowCrypt" \ --rules trailingSpace, blankLinesAtEndOfScope, consecutiveBlankLines, consecutiveSpaces, \ duplicateImports, initCoderUnavailable, isEmpty, leadingDelimiters, preferKeyPath, redundantBreak, \ redundantExtensionACL, redundantFileprivate, redundantGet, redundantLet, redundantLetError, \ redundantNilInit, redundantParens, redundantPattern, redundantReturn, redundantVoidReturnType, semicolons, \ sortedImports, spaceAroundBraces, spaceAroundBrackets, spaceAroundGenerics, spaceInsideBraces, spaceInsideGenerics, \ - strongifiedSelf, trailingClosures, void, wrapArguments --wraparguments, - --swiftversion 5 + strongifiedSelf, trailingClosures, void else echo "warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat" brew install swiftformat From a04bddf2dfc0c632c2966aa08c7dce5e703fc560 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Thu, 13 May 2021 12:56:02 +0300 Subject: [PATCH 8/8] Update attachments functionality --- FlowCrypt.xcodeproj/project.pbxproj | 2 - .../Msg/MessageViewController.swift | 33 ++--- .../Msg/MessageViewDecorator.swift | 3 +- .../Message Provider/MessageService.swift | 8 +- FlowCryptUI/Nodes/AttachmentNode.swift | 118 ++++-------------- 5 files changed, 48 insertions(+), 116 deletions(-) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 5023c5839..a721e0631 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -27,7 +27,6 @@ 32DCAF95A6A329C3136B1C8E /* Imap+msg.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA55C094E9745AA1FD210 /* Imap+msg.swift */; }; 32DCAF9DA9EC47798DF8BB73 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA9701B2D5052225A0414 /* SignInViewController.swift */; }; 50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; - 5084BEA6264ABB3100755E52 /* AttachmentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50531BE32629B9A80039BAE9 /* AttachmentNode.swift */; }; 5A39F42D239EC321001F4607 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42C239EC321001F4607 /* SettingsViewController.swift */; }; 5A39F430239EC396001F4607 /* SettingsViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F42F239EC396001F4607 /* SettingsViewDecorator.swift */; }; 5A39F437239ECC23001F4607 /* KeySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A39F436239ECC23001F4607 /* KeySettingsViewController.swift */; }; @@ -2342,7 +2341,6 @@ D2CDC3D22402D4DA002B045F /* UIViewControllerExtensions.swift in Sources */, D297990D2444A76D004A3E31 /* UserObject+Empty.swift in Sources */, D2D27B79248A8694007346FA /* BigIntExtension.swift in Sources */, - 5084BEA6264ABB3100755E52 /* AttachmentNode.swift in Sources */, 9F003D6D25EA8F3200EB38C0 /* UserAccountService.swift in Sources */, D29AFFF92409767F00C1387D /* GoogleContactsResponse.swift in Sources */, 9F003D6125E1B4ED00EB38C0 /* TrashFolderProvider.swift in Sources */, diff --git a/FlowCrypt/Controllers/Msg/MessageViewController.swift b/FlowCrypt/Controllers/Msg/MessageViewController.swift index 02bd4adb1..e23f09675 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewController.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewController.swift @@ -12,14 +12,14 @@ final class MessageViewController: TableNodeViewController { var bodyMessage: Data? var path = "" } - + enum Sections: Int, CaseIterable { case main, attributes } enum Parts: Int, CaseIterable { case sender, subject, text - + var indexPath: IndexPath { IndexPath(row: rawValue, section: 0) } @@ -135,7 +135,7 @@ extension MessageViewController { private func fetchDecryptAndRenderMsg() { guard let input = input else { return } showSpinner("loading_title".localized, isUserInteractionEnabled: true) - + Promise { [weak self] in guard let self = self else { return } let promise = self.messageService.getMessage(with: input.objMessage, folder: input.path) @@ -322,7 +322,7 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { func numberOfSections(in tableNode: ASTableNode) -> Int { Sections.allCases.count } - + func tableNode(_: ASTableNode, numberOfRowsInSection section: Int) -> Int { guard let section = Sections(rawValue: section) else { return 0 @@ -334,11 +334,11 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { return fetchedMessage.attachments.count } } - + func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { - return { [weak self] in + { [weak self] in guard let self = self, let section = Sections(rawValue: indexPath.section) else { return ASCellNode() } - + switch section { case .main: return self.mainSectionNode(for: indexPath.row) @@ -347,10 +347,10 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { } } } - + private func mainSectionNode(for index: Int) -> ASCellNode { guard let part = Parts(rawValue: index) else { return ASCellNode() } - + let senderTitle = decorator.attributed( title: input?.objMessage.sender ?? "(unknown sender)" ) @@ -360,8 +360,8 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { let time = decorator.attributed( date: input?.objMessage.date ) - - switch part{ + + switch part { case .sender: return MessageSenderNode(senderTitle) { [weak self] in self?.handleReplyTap() @@ -373,11 +373,12 @@ extension MessageViewController: ASTableDelegate, ASTableDataSource { return MessageTextSubjectNode(messageInput) } } - + private func attachmentNode(for index: Int) -> ASCellNode { - let attachment = fetchedMessage.attachments[index] - return AttachmentNode(input: .init(msgAttachment: attachment)) { [weak self] in - self?.handleAttachmentTap() - } + AttachmentNode( + input: .init( + msgAttachment: fetchedMessage.attachments[index] + ) + ) } } diff --git a/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift b/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift index 5f1f18691..e30b49aef 100644 --- a/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift +++ b/FlowCrypt/Controllers/Msg/MessageViewDecorator.swift @@ -6,6 +6,7 @@ // Copyright © 2019 FlowCrypt Limited. All rights reserved. // +import FlowCryptUI import UIKit struct MessageViewDecorator { @@ -29,7 +30,7 @@ struct MessageViewDecorator { func attributed(text: String?, color: UIColor) -> NSAttributedString { (text ?? "").attributed(.regular(17), color: color) } - + func attributedMessage(from fetchedMessage: FetchedMessage) -> NSAttributedString { fetchedMessage.text.attributed() } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift index 935240a6e..4c86e8983 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift @@ -20,7 +20,7 @@ struct FetchedMessage { enum MessageType { case error, encrypted, plain } - + let rawMimeData: Data let text: String let attachments: [MessageAttachment] @@ -42,7 +42,7 @@ final class MessageService { private let messageProvider: MessageProvider private let dataService: DataServiceType & KeyDataServiceType private let core: Core - + init( messageProvider: MessageProvider = MailProvider.shared.messageProvider, dataService: DataServiceType & KeyDataServiceType = DataService.shared, @@ -52,7 +52,7 @@ final class MessageService { self.dataService = dataService self.core = core } - + func getMessage(with input: Message, folder: String) -> Promise { Promise { [weak self] resolve, reject in guard let self = self else { return } @@ -82,7 +82,7 @@ final class MessageService { let messageType: FetchedMessage.MessageType let text: String - + if let decryptErrBlock = decryptErrBlocks.first { let rawMsg = decryptErrBlock.content let err = decryptErrBlock.decryptErr?.error diff --git a/FlowCryptUI/Nodes/AttachmentNode.swift b/FlowCryptUI/Nodes/AttachmentNode.swift index abdf940d9..d80e529e0 100644 --- a/FlowCryptUI/Nodes/AttachmentNode.swift +++ b/FlowCryptUI/Nodes/AttachmentNode.swift @@ -2,117 +2,42 @@ // AttachmentNode.swift // FlowCryptUI // -// Created by QSD BiH on 16. 4. 2021.. -// Copyright © 2021 FlowCrypt Limited. All rights reserved. -// import AsyncDisplayKit -<<<<<<< HEAD public final class AttachmentNode: CellNode { public struct Input { let name: NSAttributedString let size: NSAttributedString public init(name: NSAttributedString, size: NSAttributedString) { -======= -public struct Attachment { - var name, size: String - - public init( - name: String, - size: Int - ) { - self.name = name - self.size = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .file) - } -} - -public final class AttachmentsNode: CellNode { - public struct Input { - let name: String - let size: String - - public init( - name: String, - size: String - ) { ->>>>>>> 9632392b97ddd577aa5838c2f24ec75ad4b046e1 self.name = name self.size = size } } -<<<<<<< HEAD -======= - - private var attachmentNodes: [AttachmentNode] = [] - private var onTap: (() -> Void)? - - public init(attachments: [Attachment], onTap: (() -> Void)?) { - super.init() - self.onTap = onTap - attachmentNodes = attachments.map { AttachmentNode(input: AttachmentNode.Input(name: $0.name, size: $0.size), - onTap: { - self.onTap?() - }) - } - } - - public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { - return ASInsetLayoutSpec( - insets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), - child: ASStackLayoutSpec( - direction: .vertical, - spacing: 8, - justifyContent: .start, - alignItems: .stretch, - children: attachmentNodes)) - } -} - -public final class AttachmentNode: CellNode { - public struct Input { - var name, size: String - } - ->>>>>>> 9632392b97ddd577aa5838c2f24ec75ad4b046e1 private let titleNode = ASTextNode() private let subtitleNode = ASTextNode2() private let imageNode = ASImageNode() private let buttonNode = ASButtonNode() - - private var onTap: (() -> Void)? - -<<<<<<< HEAD - init(input: Input, onTap: (() -> Void)?) { -======= - public init(input: Input, onTap: (() -> Void)?) { ->>>>>>> 9632392b97ddd577aa5838c2f24ec75ad4b046e1 + private let borderNode = ASDisplayNode() + + public init(input: Input) { super.init() - self.onTap = onTap - - self.borderWidth = 1.0 - self.cornerRadius = 8.0 - self.borderColor = UIColor.lightGray.cgColor + + borderNode.borderWidth = 1.0 + borderNode.cornerRadius = 8.0 + borderNode.borderColor = UIColor.lightGray.cgColor imageNode.tintColor = .gray buttonNode.tintColor = .gray - + imageNode.image = UIImage(named: "paperclip")?.tinted(.gray) buttonNode.setImage(UIImage(named: "download")?.tinted(.gray), for: .normal) - buttonNode.addTarget(self, action: #selector(tapHandle), forControlEvents: .touchUpInside) -<<<<<<< HEAD titleNode.attributedText = input.name subtitleNode.attributedText = input.size } -======= - titleNode.attributedText = NSAttributedString.text(from: input.name, style: .regular(18), color: .gray, alignment: .left) - subtitleNode.attributedText = NSAttributedString.text(from: input.size, style: .medium(12), color: .gray, alignment: .left) - } - ->>>>>>> 9632392b97ddd577aa5838c2f24ec75ad4b046e1 public override func layoutSpecThatFits(_: ASSizeRange) -> ASLayoutSpec { let verticalStack = ASStackLayoutSpec.vertical() verticalStack.spacing = 3 @@ -120,11 +45,7 @@ public final class AttachmentNode: CellNode { verticalStack.style.flexGrow = 1.0 verticalStack.children = [titleNode, subtitleNode] -<<<<<<< HEAD -======= - ->>>>>>> 9632392b97ddd577aa5838c2f24ec75ad4b046e1 let finalSpec = ASStackLayoutSpec( direction: .horizontal, spacing: 10, @@ -133,13 +54,24 @@ public final class AttachmentNode: CellNode { children: [imageNode, verticalStack, buttonNode] ) - return ASInsetLayoutSpec( - insets: UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20), + let borderInset = UIEdgeInsets.side(8) + + let resultSpec = ASInsetLayoutSpec( + insets: UIEdgeInsets( + top: 8 + borderInset.top, + left: 16 + borderInset.left, + bottom: 8 + borderInset.bottom, + right: 17 + borderInset.right + ), child: finalSpec ) - } - - @objc private func tapHandle() { - onTap?() + + return ASOverlayLayoutSpec( + child: resultSpec, + overlay: ASInsetLayoutSpec( + insets: borderInset, + child: borderNode + ) + ) } }