|
|
@ -6,27 +6,52 @@ import Foundation
|
|
|
|
import SignalServiceKit
|
|
|
|
import SignalServiceKit
|
|
|
|
import SignalMessaging
|
|
|
|
import SignalMessaging
|
|
|
|
import Reachability
|
|
|
|
import Reachability
|
|
|
|
|
|
|
|
import ContactsUI
|
|
|
|
|
|
|
|
|
|
|
|
class ContactViewController: OWSViewController {
|
|
|
|
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
|
|
|
|
|
|
|
|
//, ContactsViewHelperDelegate
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
let TAG = "[ContactView]"
|
|
|
|
let TAG = "[ContactView]"
|
|
|
|
|
|
|
|
|
|
|
|
enum ContactViewMode {
|
|
|
|
enum ContactViewMode {
|
|
|
|
case systemContactWithSignal,
|
|
|
|
case systemContactWithSignal,
|
|
|
|
systemContactWithoutSignal,
|
|
|
|
systemContactWithoutSignal,
|
|
|
|
nonSystemContactWithSignal,
|
|
|
|
nonSystemContact,
|
|
|
|
nonSystemContactWithoutSignal,
|
|
|
|
|
|
|
|
noPhoneNumber,
|
|
|
|
noPhoneNumber,
|
|
|
|
unknown
|
|
|
|
unknown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ContactLookupMode {
|
|
|
|
|
|
|
|
case notLookingUp,
|
|
|
|
|
|
|
|
lookingUp,
|
|
|
|
|
|
|
|
lookedUpNoAccount,
|
|
|
|
|
|
|
|
lookedUpHasAccount
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var hasLoadedView = false
|
|
|
|
private var hasLoadedView = false
|
|
|
|
|
|
|
|
|
|
|
|
private var viewMode = ContactViewMode.unknown {
|
|
|
|
private var viewMode = ContactViewMode.unknown {
|
|
|
@ -39,16 +64,6 @@ class ContactViewController: OWSViewController {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private var lookupMode = ContactLookupMode.notLookingUp {
|
|
|
|
|
|
|
|
didSet {
|
|
|
|
|
|
|
|
SwiftAssertIsOnMainThread(#function)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if oldValue != lookupMode && hasLoadedView {
|
|
|
|
|
|
|
|
updateContent()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let contactsManager: OWSContactsManager
|
|
|
|
let contactsManager: OWSContactsManager
|
|
|
|
|
|
|
|
|
|
|
|
var reachability: Reachability?
|
|
|
|
var reachability: Reachability?
|
|
|
@ -59,6 +74,8 @@ class ContactViewController: OWSViewController {
|
|
|
|
|
|
|
|
|
|
|
|
private let contact: OWSContact
|
|
|
|
private let contact: OWSContact
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// private var contactsViewHelper : ContactsViewHelper!
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Initializers
|
|
|
|
// MARK: - Initializers
|
|
|
|
|
|
|
|
|
|
|
|
@available(*, unavailable, message: "use init(call:) constructor instead.")
|
|
|
|
@available(*, unavailable, message: "use init(call:) constructor instead.")
|
|
|
@ -69,9 +86,12 @@ class ContactViewController: OWSViewController {
|
|
|
|
required init(contact: OWSContact) {
|
|
|
|
required init(contact: OWSContact) {
|
|
|
|
contactsManager = Environment.current().contactsManager
|
|
|
|
contactsManager = Environment.current().contactsManager
|
|
|
|
self.contact = contact
|
|
|
|
self.contact = contact
|
|
|
|
|
|
|
|
self.scrollView = UIScrollView()
|
|
|
|
|
|
|
|
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// contactsViewHelper = ContactsViewHelper(delegate:self)
|
|
|
|
|
|
|
|
|
|
|
|
tryToDetermineMode()
|
|
|
|
tryToDetermineMode()
|
|
|
|
|
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in
|
|
|
|
NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in
|
|
|
@ -96,6 +116,8 @@ class ContactViewController: OWSViewController {
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UIUtil.applySignalAppearence()
|
|
|
|
|
|
|
|
|
|
|
|
self.becomeFirstResponder()
|
|
|
|
self.becomeFirstResponder()
|
|
|
|
|
|
|
|
|
|
|
|
contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in
|
|
|
|
contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in
|
|
|
@ -107,11 +129,28 @@ class ContactViewController: OWSViewController {
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UIUtil.applySignalAppearence()
|
|
|
|
|
|
|
|
|
|
|
|
self.becomeFirstResponder()
|
|
|
|
self.becomeFirstResponder()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var scrollView: UIScrollView
|
|
|
|
|
|
|
|
|
|
|
|
override func loadView() {
|
|
|
|
override func loadView() {
|
|
|
|
|
|
|
|
// scrollView.dir
|
|
|
|
|
|
|
|
// self.view = scrollView
|
|
|
|
|
|
|
|
// let rootView = UIView()
|
|
|
|
super.loadView()
|
|
|
|
super.loadView()
|
|
|
|
|
|
|
|
// self.view.layoutMargins = .zero
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// self.scrollView = scrollView
|
|
|
|
|
|
|
|
// scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
|
|
|
|
|
|
self.view.addSubview(scrollView)
|
|
|
|
|
|
|
|
scrollView.layoutMargins = .zero
|
|
|
|
|
|
|
|
scrollView.autoPinWidthToSuperview()
|
|
|
|
|
|
|
|
scrollView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
|
|
|
|
|
|
|
|
scrollView.autoPin(toBottomLayoutGuideOf: self, withInset: 0)
|
|
|
|
|
|
|
|
|
|
|
|
self.view.backgroundColor = UIColor.white
|
|
|
|
self.view.backgroundColor = UIColor.white
|
|
|
|
|
|
|
|
|
|
|
|
updateContent()
|
|
|
|
updateContent()
|
|
|
@ -138,41 +177,7 @@ class ContactViewController: OWSViewController {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch lookupMode {
|
|
|
|
viewMode = .nonSystemContact
|
|
|
|
case .notLookingUp:
|
|
|
|
|
|
|
|
lookupMode = .lookingUp
|
|
|
|
|
|
|
|
viewMode = .unknown
|
|
|
|
|
|
|
|
ContactsUpdater.shared().lookupIdentifiers([firstPhoneNumber.phoneNumber], success: { [weak self] (signalRecipients) in
|
|
|
|
|
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let hasSignalAccount = signalRecipients.filter({ (signalRecipient) -> Bool in
|
|
|
|
|
|
|
|
return signalRecipient.recipientId() == firstPhoneNumber.phoneNumber
|
|
|
|
|
|
|
|
}).count > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if hasSignalAccount {
|
|
|
|
|
|
|
|
strongSelf.lookupMode = .lookedUpHasAccount
|
|
|
|
|
|
|
|
strongSelf.tryToDetermineMode()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
strongSelf.lookupMode = .lookedUpNoAccount
|
|
|
|
|
|
|
|
strongSelf.tryToDetermineMode()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}) { [weak self] (error) in
|
|
|
|
|
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
|
|
|
|
Logger.error("\(strongSelf.logTag) error looking up contact: \(error)")
|
|
|
|
|
|
|
|
strongSelf.lookupMode = .notLookingUp
|
|
|
|
|
|
|
|
strongSelf.tryToDetermineModeRetry()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
case .lookingUp:
|
|
|
|
|
|
|
|
viewMode = .unknown
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
case .lookedUpNoAccount:
|
|
|
|
|
|
|
|
viewMode = .nonSystemContactWithoutSignal
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
case .lookedUpHasAccount:
|
|
|
|
|
|
|
|
viewMode = .nonSystemContactWithSignal
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func tryToDetermineModeRetry() {
|
|
|
|
private func tryToDetermineModeRetry() {
|
|
|
@ -186,7 +191,9 @@ class ContactViewController: OWSViewController {
|
|
|
|
private func updateContent() {
|
|
|
|
private func updateContent() {
|
|
|
|
SwiftAssertIsOnMainThread(#function)
|
|
|
|
SwiftAssertIsOnMainThread(#function)
|
|
|
|
|
|
|
|
|
|
|
|
for subview in self.view.subviews {
|
|
|
|
let rootView = self.scrollView
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for subview in rootView.subviews {
|
|
|
|
subview.removeFromSuperview()
|
|
|
|
subview.removeFromSuperview()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -194,9 +201,10 @@ class ContactViewController: OWSViewController {
|
|
|
|
let topView = UIView.container()
|
|
|
|
let topView = UIView.container()
|
|
|
|
topView.backgroundColor = UIColor(rgbHex: 0xefeff4)
|
|
|
|
topView.backgroundColor = UIColor(rgbHex: 0xefeff4)
|
|
|
|
topView.preservesSuperviewLayoutMargins = true
|
|
|
|
topView.preservesSuperviewLayoutMargins = true
|
|
|
|
self.view.addSubview(topView)
|
|
|
|
rootView.addSubview(topView)
|
|
|
|
topView.autoPinEdge(toSuperviewEdge: .top)
|
|
|
|
topView.autoPinEdge(toSuperviewEdge: .top)
|
|
|
|
topView.autoPinWidthToSuperview()
|
|
|
|
topView.autoPinEdge(.left, to: .left, of: self.view, withOffset: 0)
|
|
|
|
|
|
|
|
topView.autoPinEdge(.right, to: .right, of: self.view, withOffset: 0)
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Use actual avatar.
|
|
|
|
// TODO: Use actual avatar.
|
|
|
|
let avatarSize = CGFloat(100)
|
|
|
|
let avatarSize = CGFloat(100)
|
|
|
@ -204,14 +212,14 @@ class ContactViewController: OWSViewController {
|
|
|
|
avatarView.backgroundColor = UIColor.ows_materialBlue
|
|
|
|
avatarView.backgroundColor = UIColor.ows_materialBlue
|
|
|
|
avatarView.layer.cornerRadius = avatarSize * 0.5
|
|
|
|
avatarView.layer.cornerRadius = avatarSize * 0.5
|
|
|
|
topView.addSubview(avatarView)
|
|
|
|
topView.addSubview(avatarView)
|
|
|
|
avatarView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
|
|
|
|
avatarView.autoPin(toTopLayoutGuideOf: self, withInset: 10)
|
|
|
|
avatarView.autoHCenterInSuperview()
|
|
|
|
avatarView.autoHCenterInSuperview()
|
|
|
|
avatarView.autoSetDimension(.width, toSize: avatarSize)
|
|
|
|
avatarView.autoSetDimension(.width, toSize: avatarSize)
|
|
|
|
avatarView.autoSetDimension(.height, toSize: avatarSize)
|
|
|
|
avatarView.autoSetDimension(.height, toSize: avatarSize)
|
|
|
|
|
|
|
|
|
|
|
|
let nameLabel = UILabel()
|
|
|
|
let nameLabel = UILabel()
|
|
|
|
nameLabel.text = contact.displayName
|
|
|
|
nameLabel.text = contact.displayName
|
|
|
|
nameLabel.font = UIFont.ows_dynamicTypeTitle3
|
|
|
|
nameLabel.font = UIFont.ows_dynamicTypeTitle1
|
|
|
|
nameLabel.textColor = UIColor.black
|
|
|
|
nameLabel.textColor = UIColor.black
|
|
|
|
nameLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
nameLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
nameLabel.textAlignment = .center
|
|
|
|
nameLabel.textAlignment = .center
|
|
|
@ -225,12 +233,12 @@ class ContactViewController: OWSViewController {
|
|
|
|
if let firstPhoneNumber = contact.phoneNumbers?.first {
|
|
|
|
if let firstPhoneNumber = contact.phoneNumbers?.first {
|
|
|
|
let phoneNumberLabel = UILabel()
|
|
|
|
let phoneNumberLabel = UILabel()
|
|
|
|
phoneNumberLabel.text = firstPhoneNumber.phoneNumber
|
|
|
|
phoneNumberLabel.text = firstPhoneNumber.phoneNumber
|
|
|
|
phoneNumberLabel.font = UIFont.ows_dynamicTypeCaption1
|
|
|
|
phoneNumberLabel.font = UIFont.ows_dynamicTypeCaption2
|
|
|
|
phoneNumberLabel.textColor = UIColor.black
|
|
|
|
phoneNumberLabel.textColor = UIColor.black
|
|
|
|
phoneNumberLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
phoneNumberLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
phoneNumberLabel.textAlignment = .center
|
|
|
|
phoneNumberLabel.textAlignment = .center
|
|
|
|
topView.addSubview(phoneNumberLabel)
|
|
|
|
topView.addSubview(phoneNumberLabel)
|
|
|
|
phoneNumberLabel.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 10)
|
|
|
|
phoneNumberLabel.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 5)
|
|
|
|
phoneNumberLabel.autoPinLeadingToSuperviewMargin()
|
|
|
|
phoneNumberLabel.autoPinLeadingToSuperviewMargin()
|
|
|
|
phoneNumberLabel.autoPinTrailingToSuperviewMargin()
|
|
|
|
phoneNumberLabel.autoPinTrailingToSuperviewMargin()
|
|
|
|
lastView = phoneNumberLabel
|
|
|
|
lastView = phoneNumberLabel
|
|
|
@ -238,14 +246,16 @@ class ContactViewController: OWSViewController {
|
|
|
|
|
|
|
|
|
|
|
|
switch viewMode {
|
|
|
|
switch viewMode {
|
|
|
|
case .systemContactWithSignal:
|
|
|
|
case .systemContactWithSignal:
|
|
|
|
|
|
|
|
// Show actions buttons for system contacts with a Signal account.
|
|
|
|
break
|
|
|
|
break
|
|
|
|
case .systemContactWithoutSignal:
|
|
|
|
case .systemContactWithoutSignal:
|
|
|
|
|
|
|
|
// Show invite button for system contacts without a Signal account.
|
|
|
|
break
|
|
|
|
break
|
|
|
|
case .nonSystemContactWithSignal:
|
|
|
|
case .nonSystemContact:
|
|
|
|
break
|
|
|
|
// Show no action buttons for contacts not in user's device contacts.
|
|
|
|
case .nonSystemContactWithoutSignal:
|
|
|
|
|
|
|
|
break
|
|
|
|
break
|
|
|
|
case .noPhoneNumber:
|
|
|
|
case .noPhoneNumber:
|
|
|
|
|
|
|
|
// Show no action buttons for contacts without a phone number.
|
|
|
|
break
|
|
|
|
break
|
|
|
|
case .unknown:
|
|
|
|
case .unknown:
|
|
|
|
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
|
|
|
|
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
|
|
|
@ -256,7 +266,149 @@ class ContactViewController: OWSViewController {
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lastView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 10)
|
|
|
|
lastView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 15)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let bottomView = UIView.container()
|
|
|
|
|
|
|
|
bottomView.backgroundColor = UIColor.white
|
|
|
|
|
|
|
|
bottomView.layoutMargins = .zero
|
|
|
|
|
|
|
|
bottomView.preservesSuperviewLayoutMargins = false
|
|
|
|
|
|
|
|
rootView.addSubview(bottomView)
|
|
|
|
|
|
|
|
bottomView.autoPinEdge(.top, to: .bottom, of: topView, withOffset: 0)
|
|
|
|
|
|
|
|
bottomView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
|
|
|
|
bottomView.autoPinEdge(.left, to: .left, of: self.view, withOffset: 0)
|
|
|
|
|
|
|
|
bottomView.autoPinEdge(.right, to: .right, of: self.view, withOffset: 0)
|
|
|
|
|
|
|
|
bottomView.setContentHuggingVerticalLow()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var lastRow: UIView?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let addSpacerRow = {
|
|
|
|
|
|
|
|
guard let prevRow = lastRow else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) missing last row")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let row = UIView()
|
|
|
|
|
|
|
|
row.backgroundColor = UIColor(rgbHex: 0xdedee1)
|
|
|
|
|
|
|
|
bottomView.addSubview(row)
|
|
|
|
|
|
|
|
row.autoSetDimension(.height, toSize: 1)
|
|
|
|
|
|
|
|
row.autoPinLeadingToSuperviewMargin(withInset: self.hMargin)
|
|
|
|
|
|
|
|
row.autoPinTrailingToSuperviewMargin()
|
|
|
|
|
|
|
|
row.autoPinEdge(.top, to: .bottom, of: prevRow, withOffset: 0)
|
|
|
|
|
|
|
|
lastRow = row
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let addRow: ((UIView) -> Void) = { (row) in
|
|
|
|
|
|
|
|
if lastRow != nil {
|
|
|
|
|
|
|
|
addSpacerRow()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bottomView.addSubview(row)
|
|
|
|
|
|
|
|
row.autoPinLeadingToSuperviewMargin()
|
|
|
|
|
|
|
|
row.autoPinTrailingToSuperviewMargin()
|
|
|
|
|
|
|
|
if let lastRow = lastRow {
|
|
|
|
|
|
|
|
row.autoPinEdge(.top, to: .bottom, of: lastRow, withOffset: 0)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
row.autoPinEdge(toSuperviewEdge: .top, withInset: 0)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
lastRow = row
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.labelString(),
|
|
|
|
|
|
|
|
value: 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.labelString(),
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private let hMargin = CGFloat(10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func createActionRow(labelText: String, action: Selector) -> UIView {
|
|
|
|
|
|
|
|
let row = UIView()
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func createNameValueRow(name: String, value: String, actionBlock : @escaping () -> Void) -> UIView {
|
|
|
|
|
|
|
|
let row = TappableView(actionBlock: actionBlock)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// acceptIncomingButton = createButton(image: #imageLiteral(resourceName: "call-active-wide"),
|
|
|
|
// acceptIncomingButton = createButton(image: #imageLiteral(resourceName: "call-active-wide"),
|
|
|
@ -299,5 +451,127 @@ class ContactViewController: OWSViewController {
|
|
|
|
// self.dismiss(animated: true, completion: completion)
|
|
|
|
// self.dismiss(animated: true, completion: completion)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - ContactsViewHelperDelegate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @objc
|
|
|
|
|
|
|
|
// public func contactsViewHelperDidUpdateContacts() {
|
|
|
|
|
|
|
|
// Logger.info("\(self.TAG) \(#function)")
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
// // Do nothing
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func presentNewContactView() {
|
|
|
|
|
|
|
|
guard contactsManager.supportsContactEditing else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) Contact editing not supported")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// contactsViewHelper.presentContactViewController
|
|
|
|
|
|
|
|
// let contactsViewHelper = ContactsViewHelper(delegate:self)
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// TSContactThread *contactThread = (TSContactThread *)self.thread;
|
|
|
|
|
|
|
|
// [self.contactsViewHelper presentContactViewControllerForRecipientId:contactThread.contactIdentifier
|
|
|
|
|
|
|
|
// fromViewController:self
|
|
|
|
|
|
|
|
// editImmediately:YES];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guard let systemContact = OWSContacts.systemContact(for: contact) else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) Could not derive system contact.")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guard contactsManager.isSystemContactsAuthorized else {
|
|
|
|
|
|
|
|
ContactsViewHelper.presentMissingContactAccessAlertController(from: self)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let contactViewController = CNContactViewController(forNewContact: systemContact)
|
|
|
|
|
|
|
|
contactViewController.delegate = self
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.navigationController?.pushViewController(contactViewController, animated: true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// HACK otherwise CNContactViewController Navbar is shown as black.
|
|
|
|
|
|
|
|
// RADAR rdar://28433898 http://www.openradar.me/28433898
|
|
|
|
|
|
|
|
// CNContactViewController incompatible with opaque navigation bar
|
|
|
|
|
|
|
|
UIUtil.applyDefaultSystemAppearence()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func presentSelectAddToExistingContactView() {
|
|
|
|
|
|
|
|
guard contactsManager.supportsContactEditing else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) Contact editing not supported")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guard let systemContact = OWSContacts.systemContact(for: contact) else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) Could not derive system contact.")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guard contactsManager.isSystemContactsAuthorized else {
|
|
|
|
|
|
|
|
ContactsViewHelper.presentMissingContactAccessAlertController(from: self)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guard let firstPhoneNumber = contact.phoneNumbers?.first else {
|
|
|
|
|
|
|
|
owsFail("\(self.logTag) Missing phone number.")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let viewController = OWSAddToContactViewController()
|
|
|
|
|
|
|
|
viewController.configure(withRecipientId: firstPhoneNumber.phoneNumber)
|
|
|
|
|
|
|
|
self.navigationController?.pushViewController(viewController, animated: true)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - CNContactViewControllerDelegate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@objc public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
|
|
|
|
|
|
|
|
Logger.info("\(self.TAG) \(#function)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.navigationController?.popToViewController(self, animated: true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateContent()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@objc public func didFinishEditingContact() {
|
|
|
|
|
|
|
|
Logger.info("\(self.TAG) \(#function)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.navigationController?.popToViewController(self, animated: true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateContent()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|