From be4ef514161f527a9a8c0da94c46a08d59b63da7 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 30 Sep 2020 13:52:49 +1000 Subject: [PATCH] Reduce code duplication & fix minor UI issues --- .../src/Loki/Utilities/ContactUtilities.swift | 3 + .../View Controllers/NewClosedGroupVC.swift | 178 +++++------------- .../View Controllers/NewPrivateChatVC.swift | 23 ++- .../session_separator.colorset/Contents.json | 2 +- 4 files changed, 62 insertions(+), 144 deletions(-) diff --git a/Signal/src/Loki/Utilities/ContactUtilities.swift b/Signal/src/Loki/Utilities/ContactUtilities.swift index 804d5f9c9..246c62bdc 100644 --- a/Signal/src/Loki/Utilities/ContactUtilities.swift +++ b/Signal/src/Loki/Utilities/ContactUtilities.swift @@ -12,6 +12,9 @@ enum ContactUtilities { func getDisplayName(for publicKey: String) -> String { return UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey) ?? publicKey } + if let index = result.firstIndex(of: getUserHexEncodedPublicKey()) { + result.remove(at: index) + } return result.sorted { getDisplayName(for: $0) < getDisplayName(for: $1) } } } diff --git a/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift b/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift index 43a0d651d..722901942 100644 --- a/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift +++ b/Signal/src/Loki/View Controllers/NewClosedGroupVC.swift @@ -1,42 +1,32 @@ import PromiseKit -final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate, UIScrollViewDelegate { - private var selectedContacts: Set = [] +private protocol TableViewTouchDelegate { - private lazy var contacts: [String] = { - var result: [String] = [] - let storage = OWSPrimaryStorage.shared() - storage.dbReadConnection.read { transaction in - TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in - guard let thread = object as? TSContactThread, thread.shouldThreadBeVisible else { return } - let publicKey = thread.contactIdentifier() - guard UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey) != nil else { return } - // We shouldn't be able to add slave devices to groups - guard storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) == nil else { return } - result.append(publicKey) - } - } - func getDisplayName(for hexEncodedPublicKey: String) -> String { - return UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey - } - let userPublicKey = getUserHexEncodedPublicKey() - var userLinkedDevices: Set = [ userPublicKey ] - OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userPublicKey, in: transaction) - } - result = result.filter { !userLinkedDevices.contains($0) } - result = result.sorted { getDisplayName(for: $0) < getDisplayName(for: $1) } - return result - }() + func tableViewWasTouched(_ tableView: TableView) +} + +private final class TableView : UITableView { + var touchDelegate: TableViewTouchDelegate? + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + touchDelegate?.tableViewWasTouched(self) + return super.hitTest(point, with: event) + } +} + +final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegate, TableViewTouchDelegate, UITextFieldDelegate, UIScrollViewDelegate { + private let contacts = ContactUtilities.getAllContacts() + private var selectedContacts: Set = [] // MARK: Components private lazy var nameTextField = TextField(placeholder: NSLocalizedString("vc_create_closed_group_text_field_hint", comment: "")) - - private lazy var tableView: UITableView = { - let result = UITableView() + + private lazy var tableView: TableView = { + let result = TableView() result.dataSource = self result.delegate = self - result.register(Cell.self, forCellReuseIdentifier: "Cell") + result.touchDelegate = self + result.register(UserCell.self, forCellReuseIdentifier: "UserCell") result.separatorStyle = .none result.backgroundColor = .clear result.isScrollEnabled = false @@ -58,6 +48,10 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat doneButton.tintColor = Colors.text navigationItem.rightBarButtonItem = doneButton // Set up content + setUpViewHierarchy() + } + + private func setUpViewHierarchy() { if !contacts.isEmpty { let mainStackView = UIStackView() mainStackView.axis = .vertical @@ -70,8 +64,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat nameTextFieldContainer.pin(.bottom, to: .bottom, of: nameTextField, withInset: Values.largeSpacing) mainStackView.addArrangedSubview(nameTextFieldContainer) let separator = UIView() - let alpha: CGFloat = isLightMode ? 0.2 : 1 - separator.backgroundColor = Colors.separator.withAlphaComponent(alpha) + separator.backgroundColor = Colors.separator separator.set(.height, to: Values.separatorThickness) mainStackView.addArrangedSubview(separator) tableView.set(.height, to: CGFloat(contacts.count * 67)) // A cell is exactly 67 points high @@ -106,16 +99,18 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat } } - // MARK: Data + // MARK: Table View Data Source func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return contacts.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell - let contact = contacts[indexPath.row] - cell.hexEncodedPublicKey = contact - cell.hasTick = selectedContacts.contains(contact) + let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell") as! UserCell + let publicKey = contacts[indexPath.row] + cell.publicKey = publicKey + let isSelected = selectedContacts.contains(publicKey) + cell.accessory = .tick(isSelected: isSelected) + cell.update() return cell } @@ -124,10 +119,13 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat crossfadeLabel.text = textField.text!.isEmpty ? NSLocalizedString("vc_create_closed_group_title", comment: "") : textField.text! } - func scrollViewDidScroll(_ scrollView: UIScrollView) { + fileprivate func tableViewWasTouched(_ tableView: TableView) { if nameTextField.isFirstResponder { nameTextField.resignFirstResponder() } + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { let nameTextFieldCenterY = nameTextField.convert(nameTextField.bounds.center, to: scrollView).y let tableViewOriginY = tableView.convert(tableView.bounds.origin, to: scrollView).y let titleLabelAlpha = 1 - (scrollView.contentOffset.y - nameTextFieldCenterY) / (tableViewOriginY - nameTextFieldCenterY) @@ -137,14 +135,12 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let contact = contacts[indexPath.row] - if !selectedContacts.contains(contact) { - selectedContacts.insert(contact) - } else { - selectedContacts.remove(contact) - } - guard let cell = tableView.cellForRow(at: indexPath) as? Cell else { return } - cell.hasTick = selectedContacts.contains(contact) + let publicKey = contacts[indexPath.row] + if !selectedContacts.contains(publicKey) { selectedContacts.insert(publicKey) } else { selectedContacts.remove(publicKey) } + guard let cell = tableView.cellForRow(at: indexPath) as? UserCell else { return } + let isSelected = selectedContacts.contains(publicKey) + cell.accessory = .tick(isSelected: isSelected) + cell.update() tableView.deselectRow(at: indexPath, animated: true) } @@ -256,91 +252,3 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat SignalApp.shared().homeViewController!.createNewPrivateChat() } } - -// MARK: - Cell - -private extension NewClosedGroupVC { - - final class Cell : UITableViewCell { - var hexEncodedPublicKey = "" { didSet { update() } } - var hasTick = false { didSet { update() } } - - // MARK: Components - private lazy var profilePictureView = ProfilePictureView() - - private lazy var displayNameLabel: UILabel = { - let result = UILabel() - result.textColor = Colors.text - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.lineBreakMode = .byTruncatingTail - return result - }() - - private lazy var tickImageView: UIImageView = { - let result = UIImageView() - result.contentMode = .scaleAspectFit - let size: CGFloat = 24 - result.set(.width, to: size) - result.set(.height, to: size) - return result - }() - - private lazy var separator: UIView = { - let result = UIView() - let alpha: CGFloat = isLightMode ? 0.2 : 1 - result.backgroundColor = Colors.separator.withAlphaComponent(alpha) - result.set(.height, to: Values.separatorThickness) - return result - }() - - // MARK: Initialization - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - setUpViewHierarchy() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setUpViewHierarchy() - } - - private func setUpViewHierarchy() { - // Set the cell background color - backgroundColor = Colors.cellBackground - // Set up the highlight color - let selectedBackgroundView = UIView() - selectedBackgroundView.backgroundColor = .clear // Disabled for now - self.selectedBackgroundView = selectedBackgroundView - // Set up the profile picture image view - let profilePictureViewSize = Values.smallProfilePictureSize - profilePictureView.set(.width, to: profilePictureViewSize) - profilePictureView.set(.height, to: profilePictureViewSize) - profilePictureView.size = profilePictureViewSize - // Set up the main stack view - let stackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel, tickImageView ]) - stackView.axis = .horizontal - stackView.alignment = .center - stackView.spacing = Values.mediumSpacing - stackView.set(.height, to: profilePictureViewSize) - contentView.addSubview(stackView) - stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing) - stackView.pin(.top, to: .top, of: contentView, withInset: Values.mediumSpacing) - contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.mediumSpacing) - stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing) - // Set up the separator - addSubview(separator) - separator.pin(.leading, to: .leading, of: self) - separator.pin(.bottom, to: .bottom, of: self) - separator.set(.width, to: UIScreen.main.bounds.width) - } - - // MARK: Updating - private func update() { - profilePictureView.hexEncodedPublicKey = hexEncodedPublicKey - profilePictureView.update() - displayNameLabel.text = UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey - let icon = hasTick ? #imageLiteral(resourceName: "CircleCheck") : #imageLiteral(resourceName: "Circle") - tickImageView.image = isDarkMode ? icon : icon.asTintedImage(color: Colors.text)! - } - } -} diff --git a/Signal/src/Loki/View Controllers/NewPrivateChatVC.swift b/Signal/src/Loki/View Controllers/NewPrivateChatVC.swift index 764065ffe..672d1c7da 100644 --- a/Signal/src/Loki/View Controllers/NewPrivateChatVC.swift +++ b/Signal/src/Loki/View Controllers/NewPrivateChatVC.swift @@ -49,6 +49,9 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) closeButton.tintColor = Colors.text navigationItem.leftBarButtonItem = closeButton + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDoneButtonTapped)) + doneButton.tintColor = Colors.text + navigationItem.rightBarButtonItem = doneButton // Set up page VC let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized) pages = [ enterPublicKeyVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ] @@ -118,7 +121,11 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie @objc private func close() { dismiss(animated: true, completion: nil) } - + + @objc private func handleDoneButtonTapped() { + enterPublicKeyVC.startNewPrivateChatIfPossible() + } + func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) { let hexEncodedPublicKey = string startNewPrivateChatIfPossible(with: hexEncodedPublicKey) @@ -140,7 +147,7 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie private final class EnterPublicKeyVC : UIViewController { weak var newPrivateChatVC: NewPrivateChatVC! - private lazy var userHexEncodedPublicKey: String = { + private lazy var userPublicKey: String = { if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] { return masterHexEncodedPublicKey } else { @@ -179,7 +186,7 @@ private final class EnterPublicKeyVC : UIViewController { userPublicKeyLabel.numberOfLines = 0 userPublicKeyLabel.textAlignment = .center userPublicKeyLabel.lineBreakMode = .byCharWrapping - userPublicKeyLabel.text = userHexEncodedPublicKey + userPublicKeyLabel.text = userPublicKey // Set up share button let shareButton = Button(style: .unimportant, size: .medium) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal) @@ -232,7 +239,7 @@ private final class EnterPublicKeyVC : UIViewController { // MARK: Interaction @objc private func copyPublicKey() { - UIPasteboard.general.string = userHexEncodedPublicKey + UIPasteboard.general.string = userPublicKey copyButton.isUserInteractionEnabled = false UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { self.copyButton.setTitle("Copied", for: UIControl.State.normal) @@ -241,13 +248,13 @@ private final class EnterPublicKeyVC : UIViewController { } @objc private func sharePublicKey() { - let shareVC = UIActivityViewController(activityItems: [ userHexEncodedPublicKey ], applicationActivities: nil) + let shareVC = UIActivityViewController(activityItems: [ userPublicKey ], applicationActivities: nil) newPrivateChatVC.navigationController!.present(shareVC, animated: true, completion: nil) } - @objc private func startNewPrivateChatIfPossible() { - let hexEncodedPublicKey = publicKeyTextView.text?.trimmingCharacters(in: .whitespaces) ?? "" - newPrivateChatVC.startNewPrivateChatIfPossible(with: hexEncodedPublicKey) + @objc fileprivate func startNewPrivateChatIfPossible() { + let publicKey = publicKeyTextView.text?.trimmingCharacters(in: .whitespaces) ?? "" + newPrivateChatVC.startNewPrivateChatIfPossible(with: publicKey) } } diff --git a/SignalMessaging/Loki/Redesign/Style Guide/Colors.xcassets/session_separator.colorset/Contents.json b/SignalMessaging/Loki/Redesign/Style Guide/Colors.xcassets/session_separator.colorset/Contents.json index 072516e12..9b6b9e6f2 100644 --- a/SignalMessaging/Loki/Redesign/Style Guide/Colors.xcassets/session_separator.colorset/Contents.json +++ b/SignalMessaging/Loki/Redesign/Style Guide/Colors.xcassets/session_separator.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.400", + "alpha" : "0.200", "blue" : "0x3C", "green" : "0x38", "red" : "0x36"