Skip to content

Commit

Permalink
#298 a way to import public keys in settings contacts (#2369)
Browse files Browse the repository at this point in the history
* added ability to import public key in settings page

* feat: added ui test

* fix: pr reviews
  • Loading branch information
ioanmo226 authored Sep 22, 2023
1 parent 26c05b1 commit de5c954
Show file tree
Hide file tree
Showing 19 changed files with 490 additions and 34 deletions.
28 changes: 24 additions & 4 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@
759739C02833E9E7004867CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A1D3B923FD64AB00D626D6 /* AppDelegate.swift */; };
75DCE6A02869EBC0003435F1 /* EmptyFolderCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75DCE69F2869EBC0003435F1 /* EmptyFolderCellNode.swift */; };
949ED9422303E3B400530579 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 949ED9412303E3B400530579 /* Colors.xcassets */; };
950F27042AB830640041595D /* ContactAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950F27032AB830640041595D /* ContactAddViewController.swift */; };
950F27062AB833EA0041595D /* ContactAddNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950F27052AB833EA0041595D /* ContactAddNode.swift */; };
950F27082AB84DAD0041595D /* ContactPublicKeyListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950F27072AB84DAD0041595D /* ContactPublicKeyListViewController.swift */; };
952687452A8A24C60005694B /* LetterAvatarKit in Frameworks */ = {isa = PBXBuildFile; productRef = 952687442A8A24C60005694B /* LetterAvatarKit */; };
952687472A8A2EF70005694B /* ThreadMessageDraftCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952687462A8A2EF70005694B /* ThreadMessageDraftCellNode.swift */; };
95473C1B297E61DE006C8957 /* SequenceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95473C1A297E61DE006C8957 /* SequenceExtension.swift */; };
Expand Down Expand Up @@ -350,7 +353,7 @@
D254AA6124092AC70041CAE0 /* LocalizationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F716304234FC7200031645E /* LocalizationExtensions.swift */; };
D269E02724103A20000495C3 /* ComposeViewControllerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D269E02624103A20000495C3 /* ComposeViewControllerInput.swift */; };
D26F132724509EB6009175BA /* RecipientEmailsCellNodeInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26F132624509EB6009175BA /* RecipientEmailsCellNodeInput.swift */; };
D27177452424D44200BDA9A9 /* ComposeButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56BD2F23438B4400A7371A /* ComposeButtonNode.swift */; };
D27177452424D44200BDA9A9 /* AddButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56BD2F23438B4400A7371A /* AddButtonNode.swift */; };
D27177462424D59800BDA9A9 /* InboxCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56BD3123438B5B00A7371A /* InboxCellNode.swift */; };
D27177472424D59800BDA9A9 /* TextCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56BD3523438B9D00A7371A /* TextCellNode.swift */; };
D27177492424D73000BDA9A9 /* InboxViewDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27177482424D73000BDA9A9 /* InboxViewDecorator.swift */; };
Expand Down Expand Up @@ -625,6 +628,9 @@
756BB98F285A19C100F73140 /* EmptyCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCellNode.swift; sourceTree = "<group>"; };
75DCE69F2869EBC0003435F1 /* EmptyFolderCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyFolderCellNode.swift; sourceTree = "<group>"; };
949ED9412303E3B400530579 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
950F27032AB830640041595D /* ContactAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAddViewController.swift; sourceTree = "<group>"; };
950F27052AB833EA0041595D /* ContactAddNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAddNode.swift; sourceTree = "<group>"; };
950F27072AB84DAD0041595D /* ContactPublicKeyListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPublicKeyListViewController.swift; sourceTree = "<group>"; };
952687462A8A2EF70005694B /* ThreadMessageDraftCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadMessageDraftCellNode.swift; sourceTree = "<group>"; };
95473C1A297E61DE006C8957 /* SequenceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceExtension.swift; sourceTree = "<group>"; };
9547EF202A5F106E00A048FF /* PassPhraseAlertNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseAlertNode.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -673,7 +679,7 @@
9F53CB862555E7F300C0157A /* Imap+Other.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Imap+Other.swift"; sourceTree = "<group>"; };
9F56BD2B23438A8500A7371A /* Imap+messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Imap+messages.swift"; sourceTree = "<group>"; };
9F56BD2D23438ABA00A7371A /* TapTicFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapTicFeedback.swift; sourceTree = "<group>"; };
9F56BD2F23438B4400A7371A /* ComposeButtonNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeButtonNode.swift; sourceTree = "<group>"; };
9F56BD2F23438B4400A7371A /* AddButtonNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddButtonNode.swift; sourceTree = "<group>"; };
9F56BD3123438B5B00A7371A /* InboxCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxCellNode.swift; sourceTree = "<group>"; };
9F56BD3523438B9D00A7371A /* TextCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCellNode.swift; sourceTree = "<group>"; };
9F56BD3723438C7000A7371A /* DateFormattingExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormattingExtensions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1346,6 +1352,15 @@
path = Apis;
sourceTree = "<group>";
};
950F27022AB8304C0041595D /* Contact Add */ = {
isa = PBXGroup;
children = (
950F27032AB830640041595D /* ContactAddViewController.swift */,
950F27072AB84DAD0041595D /* ContactPublicKeyListViewController.swift */,
);
path = "Contact Add";
sourceTree = "<group>";
};
95F55F9B2A7B8A470000E50F /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2207,6 +2222,7 @@
756BB98F285A19C100F73140 /* EmptyCellNode.swift */,
75DCE69F2869EBC0003435F1 /* EmptyFolderCellNode.swift */,
9547EF232A5FBA2B00A048FF /* MenuSeparatorCellNode.swift */,
950F27052AB833EA0041595D /* ContactAddNode.swift */,
);
path = "Cell Nodes";
sourceTree = "<group>";
Expand All @@ -2225,7 +2241,7 @@
9F95A3F42360778E00C80B64 /* LinkButtonNode.swift */,
5ADEDCC123A43C6800EC495E /* KeyTextCellNode.swift */,
5ADEDCB023A3EAAF00EC495E /* KeySettingCellNode.swift */,
9F56BD2F23438B4400A7371A /* ComposeButtonNode.swift */,
9F56BD2F23438B4400A7371A /* AddButtonNode.swift */,
9F1797702368EEE8002BF770 /* ButtonNode.swift */,
9F4453BF236B894D005D7D05 /* TableNode.swift */,
9F4453C1236B9273005D7D05 /* TextFieldNode.swift */,
Expand All @@ -2243,6 +2259,7 @@
D2E26F6424F1698F00612AF1 /* Contacts */ = {
isa = PBXGroup;
children = (
950F27022AB8304C0041595D /* Contact Add */,
D2E26F6E24F266E100612AF1 /* Contacts List */,
);
path = Contacts;
Expand Down Expand Up @@ -2603,6 +2620,7 @@
049E607127FDBC690089EE2A /* ComposeViewController+Drafts.swift in Sources */,
5A39F441239EF17F001F4607 /* LegalViewDecorator.swift in Sources */,
75588F8F285777AB00E595D1 /* GmailService+SendAs.swift in Sources */,
950F27082AB84DAD0041595D /* ContactPublicKeyListViewController.swift in Sources */,
9F31AB91232993F500CF87EA /* Imap+session.swift in Sources */,
C132B9D91EC30E1D00763715 /* InboxViewController.swift in Sources */,
75588F9228577BFA00E595D1 /* SendAsRealmObject.swift in Sources */,
Expand Down Expand Up @@ -2770,6 +2788,7 @@
9FA405C7265AEBA50084D133 /* SetupGenerateKeyViewController.swift in Sources */,
51FC336128C236770098313D /* ComposeViewController+Contacts.swift in Sources */,
9F6EE1552597399D0059BA51 /* BackupApiClient.swift in Sources */,
950F27042AB830640041595D /* ContactAddViewController.swift in Sources */,
9FEED1D2230DAD1E00700F8E /* InboxViewModel.swift in Sources */,
32DCAF9DA9EC47798DF8BB73 /* SignInViewController.swift in Sources */,
606FE33A2745AA2E009DA039 /* AttachmentViewController.swift in Sources */,
Expand Down Expand Up @@ -2827,7 +2846,7 @@
9F525991278E091B005940D2 /* ViewController.swift in Sources */,
511D07E3276A2DF80050417B /* ButtonWithPaddingNode.swift in Sources */,
9F7ECCA7272C3FB4008A1770 /* TextImageNode.swift in Sources */,
D27177452424D44200BDA9A9 /* ComposeButtonNode.swift in Sources */,
D27177452424D44200BDA9A9 /* AddButtonNode.swift in Sources */,
D24FAFAB2520BFAE00BF46C5 /* CheckBoxNode.swift in Sources */,
5180CB9127356D48001FC7EF /* MessageSubjectNode.swift in Sources */,
D28655952423BFF60066F52E /* SideMenuOptionalView.swift in Sources */,
Expand All @@ -2847,6 +2866,7 @@
D2E26F6824F169E300612AF1 /* ContactCellNode.swift in Sources */,
D2A9CA38242618DF00E1D898 /* LinkButtonNode.swift in Sources */,
04DE04DD27E098010055CB86 /* ComposeRecipientCellNode.swift in Sources */,
950F27062AB833EA0041595D /* ContactAddNode.swift in Sources */,
D24FAFA42520BF9100BF46C5 /* CheckBoxCircleView.swift in Sources */,
D2CDC3D72404704D002B045F /* RecipientEmailsCellNode.swift in Sources */,
5165ABCC27B526D100CCC379 /* RecipientEmailTextFieldNode.swift in Sources */,
Expand Down
12 changes: 4 additions & 8 deletions FlowCrypt/Controllers/Inbox/InboxViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class InboxViewController: ViewController {
let threadOperationsApiClient: MessagesThreadOperationsApiClient
let messageActionsHelper: MessageActionsHelper
private let refreshControl = UIRefreshControl()
private lazy var composeButton = ComposeButtonNode { [weak self] in
private lazy var composeButton = AddButtonNode(identifier: "aid-compose-message-button") { [weak self] in
self?.btnComposeTap()
}

Expand Down Expand Up @@ -210,15 +210,11 @@ extension InboxViewController {
if isSearch { return }

let offset: CGFloat = 16
let size = CGSize(width: 50, height: 50)

composeButton.frame = CGRect(
x: node.bounds.maxX - offset - size.width,
y: node.bounds.maxY - offset - size.height - safeAreaWindowInsets.bottom,
width: size.width,
height: size.height
composeButton.frame.origin = CGPoint(
x: node.bounds.maxX - offset - .addButtonSize,
y: node.bounds.maxY - offset - .addButtonSize - safeAreaWindowInsets.bottom
)
composeButton.cornerRadius = size.width / 2
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// ContactAddViewController.swift
// FlowCrypt
//
// Created by Ioan Moldovan on 9/18/23
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

import AsyncDisplayKit
import FlowCryptCommon
import FlowCryptUI

final class ContactAddViewController: TableNodeViewController {
private let appContext: AppContext
private let filesManager: FilesManagerType

init(appContext: AppContext) {
self.appContext = appContext
self.filesManager = FilesManager()
super.init(node: TableNode())
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
}

// MARK: - ASTableDelegate, ASTableDataSource
extension ContactAddViewController: ASTableDelegate, ASTableDataSource {
func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int {
return 1
}

func tableNode(_ node: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
return { [weak self] in
guard let self else { return ASCellNode() }
let node = ContactAddNode()
node.onImportFromFile = {
Task {
await self.importFromFile()
}
}
node.onImportFromClipboard = {
Task {
await self.importFromClipboard()
}
}
return node
}
}

func importFromFile() async {
await filesManager.selectFromFilesApp(from: self)
}

func importFromClipboard() async {
let data = Data((UIPasteboard.general.string ?? "").utf8)
await processKeys(for: data)
}

func processKeys(for attachmentData: Data) async {
do {
let parsedKeys = try await Core.shared.parseKeys(armoredOrBinary: attachmentData)
if parsedKeys.keyDetails.isEmpty {
showAlert(message: "no_pubkeys_found".localized)
return
}
let viewController = ContactPublicKeyListViewController(appContext: appContext, keyDetailsList: parsedKeys.keyDetails)
navigationController?.pushViewController(viewController, animated: true)
} catch {
showAlert(message: error.errorMessage)
}
}
}

extension ContactAddViewController: FilesManagerPresenter {}

// MARK: - UI
extension ContactAddViewController {
private func setupUI() {
node.delegate = self
node.dataSource = self
}
}

// MARK: - UIDocumentPickerDelegate
extension ContactAddViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let fileUrl = urls.first,
let attachment = MessageAttachment(fileURL: fileUrl),
let attachmentData = attachment.data
else {
showAlert(message: "files_picking_files_error_message".localized)
return
}
Task {
await processKeys(for: attachmentData)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// ContactPublicKeyListViewController.swift
// FlowCrypt
//
// Created by Ioan Moldovan on 9/18/23
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

import AsyncDisplayKit
import FlowCryptCommon
import FlowCryptUI

final class ContactPublicKeyListViewController: TableNodeViewController {
private let appContext: AppContext
var keyDetailsList: [KeyDetails] = []
let localContactsProvider: LocalContactsProviderType

init(appContext: AppContext, keyDetailsList: [KeyDetails]) {
self.appContext = appContext
self.localContactsProvider = LocalContactsProvider(
encryptedStorage: appContext.encryptedStorage
)
self.keyDetailsList = keyDetailsList
super.init(node: TableNode())
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
}

// MARK: - ASTableDelegate, ASTableDataSource
extension ContactPublicKeyListViewController: ASTableDelegate, ASTableDataSource {
func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int {
return keyDetailsList.count
}

func tableNode(_ node: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
return { [weak self] in
guard let self else { return ASCellNode() }
let keyDetails = keyDetailsList[indexPath.row]
let node = PublicKeyDetailNode(
input: ThreadDetailsViewController.getPublicKeyDetailInput(
for: keyDetails,
localContactsProvider: localContactsProvider
)
)
node.onImportKey = {
self.importPublicKey(indexPath: indexPath, keyDetails: keyDetails)
}
return node
}
}

private func importPublicKey(indexPath: IndexPath, keyDetails: KeyDetails) {
guard let email = keyDetails.pgpUserEmails.first else {
return
}
try? localContactsProvider.updateKey(for: email, pubKey: .init(keyDetails: keyDetails))
node.reloadRows(at: [indexPath], with: .automatic)
reloadContactList()
}

private func reloadContactList() {
if let contactsVC = navigationController?.viewControllers.first(
where: { $0 is ContactsListViewController }
) as? ContactsListViewController {
contactsVC.fetchContacts()
}
}
}

// MARK: - UI
extension ContactPublicKeyListViewController {
private func setupUI() {
node.delegate = self
node.dataSource = self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ final class ContactsListViewController: TableNodeViewController {
private var recipients: [RecipientWithSortedPubKeys] = []
private var selectedIndexPath: IndexPath?
private let appContext: AppContext
private lazy var addButton = AddButtonNode(identifier: "aid-add-contact-button") { [weak self] in
self?.addButtonTap()
}

init(
appContext: AppContext,
Expand All @@ -47,13 +50,34 @@ final class ContactsListViewController: TableNodeViewController {
super.viewWillAppear(animated)
reloadContacts()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

setupAddButton()
}

private func addButtonTap() {
let contactAddViewController = ContactAddViewController(appContext: appContext)
navigationController?.pushViewController(contactAddViewController, animated: true)
}
}

extension ContactsListViewController {
private func setupUI() {
node.delegate = self
node.dataSource = self
title = decorator.title
node.addSubnode(addButton)
}

private func setupAddButton() {
let offset: CGFloat = 16

addButton.frame.origin = CGPoint(
x: node.bounds.maxX - offset - .addButtonSize,
y: node.bounds.maxY - offset - .addButtonSize - safeAreaWindowInsets.bottom
)
}

private func reloadContacts() {
Expand All @@ -63,7 +87,7 @@ extension ContactsListViewController {
selectedIndexPath = nil
}

private func fetchContacts() {
func fetchContacts() {
Task {
do {
self.recipients = try await localContactsProvider.getAllRecipients()
Expand Down
Loading

0 comments on commit de5c954

Please sign in to comment.