From 6e18d84a101d08c26f7fde2df56ffb5d87187a36 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 3 May 2018 11:33:01 -0400 Subject: [PATCH] Add contact share approval view. --- .../ContactViewController.swift | 8 +- .../ConversationViewController.m | 32 +- .../translations/en.lproj/Localizable.strings | 3 + .../ApproveContactShareViewController.swift | 741 ++---------------- 4 files changed, 116 insertions(+), 668 deletions(-) diff --git a/Signal/src/ViewControllers/ContactViewController.swift b/Signal/src/ViewControllers/ContactViewController.swift index b0d2733b7..ecb87d694 100644 --- a/Signal/src/ViewControllers/ContactViewController.swift +++ b/Signal/src/ViewControllers/ContactViewController.swift @@ -690,10 +690,10 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate contactViewController.allowsActions = false contactViewController.allowsEditing = true contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton, style: .plain, target: self, action: #selector(didFinishEditingContact)) - contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton, - style: .plain, - target: self, - action: #selector(didFinishEditingContact)) + contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton, + style: .plain, + target: self, + action: #selector(didFinishEditingContact)) self.navigationController?.pushViewController(contactViewController, animated: true) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 7874e3011..bb4ba7a83 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2971,6 +2971,26 @@ typedef enum : NSUInteger { } } +- (void)sendContactShare:(OWSContact *)contactShare +{ + OWSAssertIsOnMainThread(); + OWSAssert(contactShare); + + DDLogVerbose(@"%@ Sending contact share.", self.logTag); + + BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; + TSOutgoingMessage *message = [ThreadUtil sendMessageWithContactShare:contactShare + inThread:self.thread + messageSender:self.messageSender + completion:nil]; + + [self messageWasSent:message]; + + if (didAddToProfileWhitelist) { + [self ensureDynamicInteractions]; + } +} + - (NSURL *)videoTempFolder { NSString *temporaryDirectory = NSTemporaryDirectory(); @@ -5059,9 +5079,19 @@ interactionControllerForAnimationController:(id String -// func isIncluded() -> Bool -// func setIsIncluded(isIncluded: Bool) -//} - -// MARK: - - class ContactShareField: NSObject { var isIncludedFlag = true -// override required init() { -// super.init() -// } - func localizedLabel() -> String { preconditionFailure("This method must be overridden") } @@ -134,8 +114,6 @@ class ContactShareFieldView: UIView { let hMargin = CGFloat(0) func createContents() { -// self.addRedBorder() - self.layoutMargins.left = 0 self.layoutMargins.right = 0 @@ -150,13 +128,11 @@ class ContactShareFieldView: UIView { checkbox.isSelected = field.isIncluded() // Disable the checkbox; the entire row is hot. checkbox.isUserInteractionEnabled = false -// checkbox.addTarget(self, action: #selector(checkboxTapped), for: .touchUpInside) addSubview(checkbox) checkbox.autoPinEdge(toSuperviewEdge: .leading, withInset: hMargin) checkbox.autoVCenterInSuperview() checkbox.setCompressionResistanceHigh() checkbox.setContentHuggingHigh() -// checkbox.addRedBorder() let nameLabel = UILabel() nameLabel.text = field.localizedLabel() @@ -176,10 +152,6 @@ class ContactShareFieldView: UIView { previewView.autoPinTrailingToSuperviewMargin(withInset: hMargin) } -// func checkboxTapped(sender: UIButton) { -// field.setIsIncluded(isIncluded: checkbox.isSelected) -// } - func wasTapped(sender: UIGestureRecognizer) { Logger.info("\(self.logTag) \(#function)") @@ -193,32 +165,13 @@ class ContactShareFieldView: UIView { // MARK: - @objc -public class ApproveContactShareViewController: OWSViewController -//, CaptioningToolbarDelegate, PlayerProgressBarDelegate, OWSVideoPlayerDelegate -{ +public class ApproveContactShareViewController: OWSViewController { weak var delegate: ApproveContactShareViewControllerDelegate? let contactsManager: OWSContactsManager let contactShare: OWSContact -// // We sometimes shrink the attachment view so that it remains somewhat visible -// // when the keyboard is presented. -// enum AttachmentViewScale { -// case fullsize, compact -// } -// -// // MARK: Properties -// -// let attachment: SignalAttachment -// private var videoPlayer: OWSVideoPlayer? -// -// private(set) var bottomToolbar: UIView! -// private(set) var mediaMessageView: MediaMessageView! -// private(set) var scrollView: UIScrollView! -// private(set) var contentContainer: UIView! -// private(set) var playVideoButton: UIView? - var fields = [ContactShareField]() var fieldViews = [ContactShareFieldView]() @@ -244,6 +197,8 @@ public class ApproveContactShareViewController: OWSViewController var fields = [ContactShareField]() var fieldViews = [ContactShareFieldView]() + // TODO: Avatar + if let phoneNumbers = contactShare.phoneNumbers { for phoneNumber in phoneNumbers { let field = ContactSharePhoneNumber(phoneNumber) @@ -284,140 +239,24 @@ public class ApproveContactShareViewController: OWSViewController self.fieldViews = fieldViews } -// -// -// -// -//// -//// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -//// -// -// -//class TappableView: UIView { -// let actionBlock : (() -> Void) -// -// // MARK: - Initializers -// -// @available(*, unavailable, message: "use init(call:) constructor instead.") -// required init?(coder aDecoder: NSCoder) { -// fatalError("Unimplemented") -// } -// -// required init(actionBlock : @escaping () -> Void) { -// self.actionBlock = actionBlock -// super.init(frame: CGRect.zero) -// -// self.isUserInteractionEnabled = true -// self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) -// } -// -// func wasTapped(sender: UIGestureRecognizer) { -// Logger.info("\(self.logTag) \(#function)") -// -// guard sender.state == .recognized else { -// return -// } -// actionBlock() -// } -//} -// -//// MARK: - -// -//class ContactViewController: OWSViewController, CNContactViewControllerDelegate { -// -// let TAG = "[ContactView]" - -// enum ContactViewMode { -// case systemContactWithSignal, -// systemContactWithoutSignal, -// nonSystemContact, -// noPhoneNumber, -// unknown -// } -// -// private var hasLoadedView = false -// -// private var viewMode = ContactViewMode.unknown { -// didSet { -// SwiftAssertIsOnMainThread(#function) -// -// if oldValue != viewMode && hasLoadedView { -// updateContent() -// } -// } -// } -// -// var reachability: Reachability? - override public var canBecomeFirstResponder: Bool { return true } -// private let contact: OWSContact -// -// // MARK: - Initializers -// -// @available(*, unavailable, message: "use init(call:) constructor instead.") -// required init?(coder aDecoder: NSCoder) { -// fatalError("Unimplemented") -// } -// -// required init(contact: OWSContact) { -// contactsManager = Environment.current().contactsManager -// self.contact = contact -// -// super.init(nibName: nil, bundle: nil) -// -// tryToDetermineMode() -// -// NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.tryToDetermineMode() -// } -// -// reachability = Reachability.forInternetConnection() -// -// NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.tryToDetermineMode() -// } -// } - // MARK: - View Lifecycle override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) updateNavigationBar() - -// UIUtil.applySignalAppearence() -// -// if let navigationController = self.navigationController { -// navigationController.isNavigationBarHidden = true -// } -// -// self.becomeFirstResponder() -// -// contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.tryToDetermineMode() -// }) } override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) -// -// UIUtil.applySignalAppearence() -// -// self.becomeFirstResponder() } override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - -// if let navigationController = self.navigationController { -// navigationController.isNavigationBarHidden = false -// } } override public func viewDidDisappear(_ animated: Bool) { @@ -432,21 +271,22 @@ public class ApproveContactShareViewController: OWSViewController self.view.preservesSuperviewLayoutMargins = false self.view.backgroundColor = UIColor.white -// self.view.backgroundColor = UIColor(rgbHex: 0xefeff4) updateContent() -// hasLoadedView = true - updateNavigationBar() } - // TODO: Show error. + // TODO: Surface error with resolution to user if not. func canShareContact() -> Bool { return contactShare.ows_isValid() } func updateNavigationBar() { + self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, + target: self, + action: #selector(didPressCancel)) + if canShareContact() { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON", comment: "Label for 'send' button in the 'attachment approval' dialog."), @@ -454,65 +294,8 @@ public class ApproveContactShareViewController: OWSViewController } else { self.navigationItem.rightBarButtonItem = nil } -// self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, -// target: self, -// action: #selector(donePressed)) - } -// private func tryToDetermineMode() { -// SwiftAssertIsOnMainThread(#function) -// -// guard phoneNumbersForContact().count > 0 else { -// viewMode = .noPhoneNumber -// return -// } -// if systemContactsWithSignalAccountsForContact().count > 0 { -// viewMode = .systemContactWithSignal -// return -// } -// if systemContactsForContact().count > 0 { -// viewMode = .systemContactWithoutSignal -// return -// } -// -// viewMode = .nonSystemContact -// } - -// private func systemContactsWithSignalAccountsForContact() -> [String] { -// SwiftAssertIsOnMainThread(#function) -// -// return phoneNumbersForContact().filter({ (phoneNumber) -> Bool in -// return contactsManager.hasSignalAccount(forRecipientId: phoneNumber) -// }) -// } -// -// private func systemContactsForContact() -> [String] { -// SwiftAssertIsOnMainThread(#function) -// -// return phoneNumbersForContact().filter({ (phoneNumber) -> Bool in -// return contactsManager.allContactsMap[phoneNumber] != nil -// }) -// } -// -// private func phoneNumbersForContact() -> [String] { -// SwiftAssertIsOnMainThread(#function) -// -// var result = [String]() -// if let phoneNumbers = contact.phoneNumbers { -// for phoneNumber in phoneNumbers { -// result.append(phoneNumber.phoneNumber) -// } -// } -// return result -// } - -// private func tryToDetermineModeRetry() { -// // Try again after a minute. -// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 60.0) { [weak self] in -// guard let strongSelf = self else { return } -// strongSelf.tryToDetermineMode() -// } -// } + } private func updateContent() { SwiftAssertIsOnMainThread(#function) @@ -526,27 +309,12 @@ public class ApproveContactShareViewController: OWSViewController subview.removeFromSuperview() } -// let topView = createTopView() -// rootView.addSubview(topView) -// topView.autoPin(toTopLayoutGuideOf: self, withInset: 0) -// topView.autoPinWidthToSuperview() - -// // This view provides a background "below the fold". -// let bottomView = UIView.container() -// bottomView.backgroundColor = UIColor.white -// self.view.addSubview(bottomView) -// bottomView.layoutMargins = .zero -// bottomView.autoPinWidthToSuperview() -// bottomView.autoPinEdge(.top, to: .bottom, of: topView, withOffset: 0) -// bottomView.autoPinEdge(toSuperviewEdge: .bottom) - let scrollView = UIScrollView() scrollView.preservesSuperviewLayoutMargins = false self.view.addSubview(scrollView) scrollView.layoutMargins = .zero scrollView.autoPinWidthToSuperview() scrollView.autoPin(toTopLayoutGuideOf: self, withInset: 0) -// scrollView.autoPinEdge(.top, to: .bottom, of: topView, withOffset: 0) scrollView.autoPinEdge(toSuperviewEdge: .bottom) let fieldsView = createFieldsView() @@ -561,138 +329,6 @@ public class ApproveContactShareViewController: OWSViewController fieldsView.autoPinEdge(toSuperviewEdge: .bottom) } -// private func createTopView() -> UIView { -// SwiftAssertIsOnMainThread(#function) -// -// let topView = UIView.container() -// topView.backgroundColor = UIColor(rgbHex: 0xefeff4) -// topView.preservesSuperviewLayoutMargins = false -// -// // Back Button -// let backButtonSize = CGFloat(50) -// let backButton = TappableView(actionBlock: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.didPressDismiss() -// }) -// backButton.autoSetDimension(.width, toSize: backButtonSize) -// backButton.autoSetDimension(.height, toSize: backButtonSize) -// topView.addSubview(backButton) -// backButton.autoPin(toTopLayoutGuideOf: self, withInset: 0) -// backButton.autoPinEdge(toSuperviewEdge: .left) -// -// let backIconName = (self.view.isRTL() ? "system_disclosure_indicator" : "system_disclosure_indicator_rtl") -// let backIconImage = UIImage(named: backIconName)?.withRenderingMode(.alwaysTemplate) -// let backIconView = UIImageView(image: backIconImage) -// backIconView.contentMode = .scaleAspectFit -// backIconView.tintColor = UIColor.black.withAlphaComponent(0.6) -// backButton.addSubview(backIconView) -// backIconView.autoCenterInSuperview() -// -// // TODO: Use actual avatar. -// let avatarSize = CGFloat(100) -// -// let avatarView = AvatarImageView() -// // TODO: What's the best colorSeed value to use? -// let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: contact.displayName, -// colorSeed: contact.displayName, -// diameter: UInt(avatarSize), -// contactsManager: contactsManager) -// avatarView.image = avatarBuilder.build() -// topView.addSubview(avatarView) -// avatarView.autoPin(toTopLayoutGuideOf: self, withInset: 20) -// avatarView.autoHCenterInSuperview() -// avatarView.autoSetDimension(.width, toSize: avatarSize) -// avatarView.autoSetDimension(.height, toSize: avatarSize) -// -// let nameLabel = UILabel() -// nameLabel.text = contact.displayName -// nameLabel.font = UIFont.ows_dynamicTypeTitle2.ows_bold() -// nameLabel.textColor = UIColor.black -// nameLabel.lineBreakMode = .byTruncatingTail -// nameLabel.textAlignment = .center -// topView.addSubview(nameLabel) -// nameLabel.autoPinEdge(.top, to: .bottom, of: avatarView, withOffset: 10) -// nameLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// var lastView: UIView = nameLabel -// -// if let firstPhoneNumber = contact.phoneNumbers?.first { -// let phoneNumberLabel = UILabel() -// phoneNumberLabel.text = PhoneNumber.bestEffortFormatE164(asLocalizedPhoneNumber: firstPhoneNumber.phoneNumber) -// phoneNumberLabel.font = UIFont.ows_dynamicTypeCaption2 -// phoneNumberLabel.textColor = UIColor.black -// phoneNumberLabel.lineBreakMode = .byTruncatingTail -// phoneNumberLabel.textAlignment = .center -// topView.addSubview(phoneNumberLabel) -// phoneNumberLabel.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 5) -// phoneNumberLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// phoneNumberLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// lastView = phoneNumberLabel -// } -// -// switch viewMode { -// case .systemContactWithSignal: -// // Show actions buttons for system contacts with a Signal account. -// let stackView = UIStackView() -// stackView.axis = .horizontal -// stackView.distribution = .fillEqually -// stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_SEND_MESSAGE", -// comment: "Label for 'sent message' button in contact view."), -// actionBlock: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.didPressSendMessage() -// })) -// stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_AUDIO_CALL", -// comment: "Label for 'audio call' button in contact view."), -// actionBlock: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.didPressAudioCall() -// })) -// stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_VIDEO_CALL", -// comment: "Label for 'video call' button in contact view."), -// actionBlock: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.didPressVideoCall() -// })) -// topView.addSubview(stackView) -// stackView.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20) -// stackView.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// stackView.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// lastView = stackView -// case .systemContactWithoutSignal: -// // Show invite button for system contacts without a Signal account. -// let inviteButton = createLargePillButton(text: NSLocalizedString("ACTION_INVITE", -// comment: "Label for 'invite' button in contact view."), -// actionBlock: { [weak self] _ in -// guard let strongSelf = self else { return } -// strongSelf.didPressInvite() -// }) -// topView.addSubview(inviteButton) -// inviteButton.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20) -// inviteButton.autoPinLeadingToSuperviewMargin(withInset: 55) -// inviteButton.autoPinTrailingToSuperviewMargin(withInset: 55) -// lastView = inviteButton -// case .nonSystemContact: -// // Show no action buttons for contacts not in user's device contacts. -// break -// case .noPhoneNumber: -// // Show no action buttons for contacts without a phone number. -// break -// case .unknown: -// let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) -// topView.addSubview(activityIndicator) -// activityIndicator.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 10) -// activityIndicator.autoHCenterInSuperview() -// lastView = activityIndicator -// break -// } -// -// lastView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 15) -// -// return topView -// } - private func createFieldsView() -> UIView { SwiftAssertIsOnMainThread(#function) @@ -732,60 +368,12 @@ public class ApproveContactShareViewController: OWSViewController lastRow = row } + addRow(createNameRow()) + for fieldView in fieldViews { addRow(fieldView) } -// if viewMode == .nonSystemContact { -// addRow(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_NEW_CONTACT", -// comment: "Label for 'new contact' button in conversation settings view."), -// action: #selector(didPressCreateNewContact))) -// -// addRow(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT", -// comment: "Label for 'new contact' button in conversation settings view."), -// action: #selector(didPressAddToExistingContact))) -// } -// -// // TODO: Not designed yet. -// // if viewMode == .systemContactWithSignal || -// // viewMode == .systemContactWithoutSignal { -// // addRow(createActionRow(labelText:NSLocalizedString("ACTION_SHARE_CONTACT", -// // comment:"Label for 'share contact' button."), -// // action:#selector(didPressShareContact))) -// // } -// -// if let phoneNumbers = contact.phoneNumbers { -// for phoneNumber in phoneNumbers { -// // TODO: Try to format the phone number nicely. -// addRow(createNameValueRow(name: phoneNumber.localizedLabel(), -// value: -// PhoneNumber.bestEffortFormatE164(asLocalizedPhoneNumber: phoneNumber.phoneNumber), -// actionBlock: { -// guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else { -// owsFail("\(ContactViewController.logTag) could not open phone number.") -// return -// } -// UIApplication.shared.openURL(url as URL) -// })) -// } -// } -// -// if let emails = contact.emails { -// for email in emails { -// addRow(createNameValueRow(name: email.localizedLabel(), -// value: email.email, -// actionBlock: { -// guard let url = NSURL(string: "mailto:\(email.email)") else { -// owsFail("\(ContactViewController.logTag) could not open email.") -// return -// } -// UIApplication.shared.openURL(url as URL) -// })) -// } -// } - - // TODO: Should we present addresses here too? How? - lastRow?.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0) return fieldsView @@ -793,143 +381,53 @@ public class ApproveContactShareViewController: OWSViewController private let hMargin = CGFloat(16) -// private func createActionRow(labelText: String, action: Selector) -> UIView { -// let row = UIView() -// row.layoutMargins.left = 0 -// row.layoutMargins.right = 0 -// row.isUserInteractionEnabled = true -// row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: action)) -// -// let label = UILabel() -// label.text = labelText -// label.font = UIFont.ows_dynamicTypeBody -// label.textColor = UIColor.ows_materialBlue -// label.lineBreakMode = .byTruncatingTail -// row.addSubview(label) -// label.autoPinTopToSuperviewMargin() -// label.autoPinBottomToSuperviewMargin() -// label.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// label.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// return row -// } + func createNameRow() -> UIView { + let nameVMargin = CGFloat(16) -// private func createNameValueRow(name: String, value: String, actionBlock : @escaping () -> Void) -> UIView { -// let row = TappableView(actionBlock: actionBlock) -// row.layoutMargins.left = 0 -// row.layoutMargins.right = 0 -// -// let nameLabel = UILabel() -// nameLabel.text = name -// nameLabel.font = UIFont.ows_dynamicTypeCaption1 -// nameLabel.textColor = UIColor.black -// nameLabel.lineBreakMode = .byTruncatingTail -// row.addSubview(nameLabel) -// nameLabel.autoPinTopToSuperviewMargin() -// nameLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// let valueLabel = UILabel() -// valueLabel.text = value -// valueLabel.font = UIFont.ows_dynamicTypeCaption1 -// valueLabel.textColor = UIColor.ows_materialBlue -// valueLabel.lineBreakMode = .byTruncatingTail -// row.addSubview(valueLabel) -// valueLabel.autoPinEdge(.top, to: .bottom, of: nameLabel, withOffset: 3) -// valueLabel.autoPinBottomToSuperviewMargin() -// valueLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// valueLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// // TODO: Should there be a disclosure icon here? -// -// return row -// } + let row = UIView() + row.layoutMargins = UIEdgeInsets(top: nameVMargin, left: 0, bottom: nameVMargin, right: 0) -// // TODO: Use real assets. -// private func createCircleActionButton(text: String, actionBlock : @escaping () -> Void) -> UIView { -// let buttonSize = CGFloat(50) -// -// let button = TappableView(actionBlock: actionBlock) -// button.layoutMargins = .zero -// button.autoSetDimension(.width, toSize: buttonSize, relation: .greaterThanOrEqual) -// -// let circleView = UIView() -// circleView.backgroundColor = UIColor.white -// circleView.autoSetDimension(.width, toSize: buttonSize) -// circleView.autoSetDimension(.height, toSize: buttonSize) -// circleView.layer.cornerRadius = buttonSize * 0.5 -// button.addSubview(circleView) -// circleView.autoPinEdge(toSuperviewEdge: .top) -// circleView.autoHCenterInSuperview() -// -// let label = UILabel() -// label.text = text -// label.font = UIFont.ows_dynamicTypeCaption2 -// label.textColor = UIColor.black -// label.lineBreakMode = .byTruncatingTail -// label.textAlignment = .center -// button.addSubview(label) -// label.autoPinEdge(.top, to: .bottom, of: circleView, withOffset: 3) -// label.autoPinEdge(toSuperviewEdge: .bottom) -// label.autoPinLeadingToSuperviewMargin() -// label.autoPinTrailingToSuperviewMargin() -// -// return button -// } - -// private func createLargePillButton(text: String, actionBlock : @escaping () -> Void) -> UIView { -// let button = TappableView(actionBlock: actionBlock) -// button.backgroundColor = UIColor.white -// button.layoutMargins = .zero -// button.autoSetDimension(.height, toSize: 45) -// button.layer.cornerRadius = 5 -// -// let label = UILabel() -// label.text = text -// label.font = UIFont.ows_dynamicTypeCaption1 -// label.textColor = UIColor.ows_materialBlue -// label.lineBreakMode = .byTruncatingTail -// label.textAlignment = .center -// button.addSubview(label) -// label.autoPinLeadingToSuperviewMargin(withInset: 20) -// label.autoPinTrailingToSuperviewMargin(withInset: 20) -// label.autoVCenterInSuperview() -// label.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) -// label.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0, relation: .greaterThanOrEqual) -// -// return button -// } + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.alignment = .center + stackView.layoutMargins = .zero + row.addSubview(stackView) + stackView.autoPinLeadingToSuperviewMargin() + stackView.autoPinTrailingToSuperviewMargin() + stackView.autoPinTopToSuperviewMargin() + stackView.autoPinBottomToSuperviewMargin() -// private func createLabeledFieldRow(name: String, value: String, actionBlock : @escaping () -> Void) -> UIView { -// let row = TappableView(actionBlock: actionBlock) -// row.layoutMargins.left = 0 -// row.layoutMargins.right = 0 -// -// let nameLabel = UILabel() -// nameLabel.text = name -// nameLabel.font = UIFont.ows_dynamicTypeCaption1 -// nameLabel.textColor = UIColor.black -// nameLabel.lineBreakMode = .byTruncatingTail -// row.addSubview(nameLabel) -// nameLabel.autoPinTopToSuperviewMargin() -// nameLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// let valueLabel = UILabel() -// valueLabel.text = value -// valueLabel.font = UIFont.ows_dynamicTypeCaption1 -// valueLabel.textColor = UIColor.ows_materialBlue -// valueLabel.lineBreakMode = .byTruncatingTail -// row.addSubview(valueLabel) -// valueLabel.autoPinEdge(.top, to: .bottom, of: nameLabel, withOffset: 3) -// valueLabel.autoPinBottomToSuperviewMargin() -// valueLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin) -// valueLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin) -// -// // TODO: Should there be a disclosure icon here? -// -// return row -// } + let nameLabel = UILabel() + nameLabel.text = contactShare.displayName + nameLabel.font = UIFont.ows_dynamicTypeBody + nameLabel.textColor = UIColor.ows_materialBlue + nameLabel.lineBreakMode = .byTruncatingTail + stackView.addArrangedSubview(nameLabel) + nameLabel.setContentHuggingHigh() + + let editNameLabel = UILabel() + editNameLabel.text = NSLocalizedString("CONTACT_EDIT_NAME_BUTTON", comment: "Label for the 'edit name' button in the contact share approval view.") + editNameLabel.font = UIFont.ows_dynamicTypeCaption1 + editNameLabel.textColor = UIColor.black + stackView.addArrangedSubview(editNameLabel) + editNameLabel.setContentHuggingHigh() + editNameLabel.setCompressionResistanceHigh() + + // Icon + let iconName = (self.view.isRTL() ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator") + let iconImage = UIImage(named: iconName)?.withRenderingMode(.alwaysTemplate) + let iconView = UIImageView(image: iconImage) + iconView.contentMode = .scaleAspectFit + iconView.tintColor = UIColor.black.withAlphaComponent(0.6) + stackView.addArrangedSubview(iconView) + iconView.setContentHuggingHigh() + iconView.setCompressionResistanceHigh() + + row.isUserInteractionEnabled = true + row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didPressEditName))) + + return row + } func previewView(forPhoneNumber phoneNumber: OWSContactPhoneNumber) -> UIView { let label = UILabel() @@ -1020,113 +518,30 @@ public class ApproveContactShareViewController: OWSViewController // MARK: - -// func didPressCreateNewContact(sender: UIGestureRecognizer) { -// Logger.info("\(self.TAG) \(#function)") -// -// guard sender.state == .recognized else { -// return -// } -// presentNewContactView() -// } -// -// func didPressAddToExistingContact(sender: UIGestureRecognizer) { -// Logger.info("\(self.TAG) \(#function)") -// -// guard sender.state == .recognized else { -// return -// } -// presentSelectAddToExistingContactView() -// } -// -// func didPressShareContact(sender: UIGestureRecognizer) { -// Logger.info("\(self.TAG) \(#function)") -// -// guard sender.state == .recognized else { -// return -// } -// // TODO: -// } -// -// func didPressSendMessage() { -// Logger.info("\(self.TAG) \(#function)") -// -// presentThreadAndPeform(action: .compose) -// } -// -// func didPressAudioCall() { -// Logger.info("\(self.TAG) \(#function)") -// -// presentThreadAndPeform(action: .audioCall) -// } -// -// func didPressVideoCall() { -// Logger.info("\(self.TAG) \(#function)") -// -// presentThreadAndPeform(action: .videoCall) -// } -// -// func presentThreadAndPeform(action: ConversationViewAction) { -// // TODO: We're taking the first Signal account id. We might -// // want to let the user select if there's more than one. -// let phoneNumbers = systemContactsWithSignalAccountsForContact() -// guard phoneNumbers.count > 0 else { -// owsFail("\(logTag) missing Signal recipient id.") -// return -// } -// guard phoneNumbers.count > 1 else { -// let recipientId = systemContactsWithSignalAccountsForContact().first! -// SignalApp.shared().presentConversation(forRecipientId: recipientId, action: action) -// return -// } -// -// showPhoneNumberPicker(phoneNumbers: phoneNumbers, completion: { (recipientId) in -// SignalApp.shared().presentConversation(forRecipientId: recipientId, action: action) -// }) -// } -// -// func didPressInvite() { -// Logger.info("\(self.TAG) \(#function)") -// -// guard MFMessageComposeViewController.canSendText() else { -// Logger.info("\(TAG) Device cannot send text") -// OWSAlerts.showErrorAlert(message: NSLocalizedString("UNSUPPORTED_FEATURE_ERROR", comment: "")) -// return -// } -// let phoneNumbers = -// phoneNumbersForContact() -// guard phoneNumbers.count > 0 else { -// owsFail("\(logTag) no phone numbers.") -// return -// } -// -// let inviteFlow = -// InviteFlow(presentingViewController: self, contactsManager: contactsManager) -// inviteFlow.sendSMSTo(phoneNumbers: phoneNumbers) -// } -// -// private func showPhoneNumberPicker(phoneNumbers: [String], completion :@escaping ((String) -> Void)) { -// -// let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) -// -// for phoneNumber in phoneNumbers { -// actionSheet.addAction(UIAlertAction(title: PhoneNumber.bestEffortFormatE164(asLocalizedPhoneNumber: phoneNumber), -// style: .default) { _ in -// completion(phoneNumber) -// }) -// } -// actionSheet.addAction(OWSAlerts.cancelAction) -// -// self.present(actionSheet, animated: true) -// } + func didPressSendButton() { + Logger.info("\(logTag) \(#function)") -// func didPressDismiss() { -// Logger.info("\(self.TAG) \(#function)") -// -// self.navigationController?.popViewController(animated: true) -// } + guard let delegate = self.delegate else { + owsFail("\(logTag) missing delegate.") + return + } - func didPressSendButton() { - Logger.info("\(logTag) \(#function)") + delegate.approveContactShare(self, didApproveContactShare: contactShare) + } + + func didPressCancel() { + Logger.info("\(logTag) \(#function)") + + guard let delegate = self.delegate else { + owsFail("\(logTag) missing delegate.") + return + } + + delegate.approveContactShare(self, didCancelContactShare: contactShare) + } + + func didPressEditName() { + Logger.info("\(logTag) \(#function)") // TODO: }