2018-05-01 19:39:48 +02:00
|
|
|
//
|
2019-03-21 15:55:04 +01:00
|
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
2018-05-01 19:39:48 +02:00
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import SignalServiceKit
|
|
|
|
import SignalMessaging
|
|
|
|
import Reachability
|
2018-05-01 21:53:51 +02:00
|
|
|
import ContactsUI
|
2018-05-02 16:08:47 +02:00
|
|
|
import MessageUI
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
class ContactViewController: OWSViewController, ContactShareViewHelperDelegate {
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
enum ContactViewMode {
|
|
|
|
case systemContactWithSignal,
|
|
|
|
systemContactWithoutSignal,
|
2018-05-01 21:53:51 +02:00
|
|
|
nonSystemContact,
|
2018-05-01 19:39:48 +02:00
|
|
|
noPhoneNumber,
|
|
|
|
unknown
|
|
|
|
}
|
|
|
|
|
|
|
|
private var hasLoadedView = false
|
|
|
|
|
|
|
|
private var viewMode = ContactViewMode.unknown {
|
|
|
|
didSet {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
if oldValue != viewMode && hasLoadedView {
|
|
|
|
updateContent()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-09 16:54:55 +02:00
|
|
|
private let contactsManager: OWSContactsManager
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-09 16:54:55 +02:00
|
|
|
private var reachability: Reachability?
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-05 04:32:29 +02:00
|
|
|
private let contactShare: ContactShareViewModel
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-09 00:07:25 +02:00
|
|
|
private var contactShareViewHelper: ContactShareViewHelper
|
2018-05-07 21:20:34 +02:00
|
|
|
|
2018-05-15 23:02:26 +02:00
|
|
|
private weak var postDismissNavigationController: UINavigationController?
|
|
|
|
|
2018-05-01 19:39:48 +02:00
|
|
|
// MARK: - Initializers
|
|
|
|
|
|
|
|
@available(*, unavailable, message: "use init(call:) constructor instead.")
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
2018-08-27 16:21:03 +02:00
|
|
|
notImplemented()
|
2018-05-01 19:39:48 +02:00
|
|
|
}
|
|
|
|
|
2018-05-25 23:28:36 +02:00
|
|
|
@objc
|
2018-05-05 02:46:00 +02:00
|
|
|
required init(contactShare: ContactShareViewModel) {
|
2018-08-31 19:22:19 +02:00
|
|
|
contactsManager = Environment.shared.contactsManager
|
2018-05-05 04:32:29 +02:00
|
|
|
self.contactShare = contactShare
|
2018-05-09 00:07:25 +02:00
|
|
|
self.contactShareViewHelper = ContactShareViewHelper(contactsManager: contactsManager)
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
|
2018-05-09 00:07:25 +02:00
|
|
|
contactShareViewHelper.delegate = self
|
2018-05-07 21:20:34 +02:00
|
|
|
|
2018-05-03 20:31:11 +02:00
|
|
|
updateMode()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in
|
|
|
|
guard let strongSelf = self else { return }
|
2018-05-03 20:31:11 +02:00
|
|
|
strongSelf.updateMode()
|
2018-05-01 19:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
reachability = Reachability.forInternetConnection()
|
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { [weak self] _ in
|
|
|
|
guard let strongSelf = self else { return }
|
2018-05-03 20:31:11 +02:00
|
|
|
strongSelf.updateMode()
|
2018-05-01 19:39:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - View Lifecycle
|
|
|
|
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
|
2018-05-09 16:54:55 +02:00
|
|
|
guard let navigationController = self.navigationController else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("navigationController was unexpectedly nil")
|
2018-05-09 16:54:55 +02:00
|
|
|
return
|
|
|
|
}
|
2018-05-15 23:02:26 +02:00
|
|
|
|
|
|
|
// self.navigationController is nil in viewWillDisappear when transition via message/call buttons
|
|
|
|
// so we maintain our own reference to restore the navigation bars.
|
|
|
|
postDismissNavigationController = navigationController
|
2018-05-09 16:54:55 +02:00
|
|
|
navigationController.isNavigationBarHidden = true
|
|
|
|
|
2018-05-01 19:39:48 +02:00
|
|
|
contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in
|
|
|
|
guard let strongSelf = self else { return }
|
2018-05-03 20:31:11 +02:00
|
|
|
strongSelf.updateMode()
|
2018-05-01 19:39:48 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
|
|
super.viewWillDisappear(animated)
|
|
|
|
|
2018-05-15 00:25:17 +02:00
|
|
|
if self.presentedViewController == nil {
|
|
|
|
// No need to do this when we're disappearing due to a modal presentation.
|
2018-05-15 23:02:26 +02:00
|
|
|
// We'll eventually return to to this view and need to hide again. But also, there is a visible
|
|
|
|
// animation glitch where the navigation bar for this view controller starts to appear while
|
|
|
|
// the whole nav stack is about to be obscured by the modal we are presenting.
|
|
|
|
guard let postDismissNavigationController = self.postDismissNavigationController else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("postDismissNavigationController was unexpectedly nil")
|
2018-05-15 00:25:17 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-15 23:02:26 +02:00
|
|
|
postDismissNavigationController.setNavigationBarHidden(false, animated: animated)
|
2018-05-09 16:54:55 +02:00
|
|
|
}
|
2018-05-02 21:43:53 +02:00
|
|
|
}
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-01 19:39:48 +02:00
|
|
|
override func loadView() {
|
|
|
|
super.loadView()
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
self.view.preservesSuperviewLayoutMargins = false
|
2018-05-04 16:30:49 +02:00
|
|
|
self.view.backgroundColor = heroBackgroundColor()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
updateContent()
|
|
|
|
|
|
|
|
hasLoadedView = true
|
|
|
|
}
|
|
|
|
|
2018-05-03 20:31:11 +02:00
|
|
|
private func updateMode() {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
guard contactShare.e164PhoneNumbers().count > 0 else {
|
2018-05-01 19:39:48 +02:00
|
|
|
viewMode = .noPhoneNumber
|
|
|
|
return
|
|
|
|
}
|
2018-05-02 16:08:47 +02:00
|
|
|
if systemContactsWithSignalAccountsForContact().count > 0 {
|
2018-05-01 19:39:48 +02:00
|
|
|
viewMode = .systemContactWithSignal
|
|
|
|
return
|
|
|
|
}
|
2018-05-02 16:08:47 +02:00
|
|
|
if systemContactsForContact().count > 0 {
|
2018-05-01 19:39:48 +02:00
|
|
|
viewMode = .systemContactWithoutSignal
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-01 21:53:51 +02:00
|
|
|
viewMode = .nonSystemContact
|
2018-05-01 19:39:48 +02:00
|
|
|
}
|
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
private func systemContactsWithSignalAccountsForContact() -> [String] {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
return contactShare.systemContactsWithSignalAccountPhoneNumbers(contactsManager)
|
2018-05-02 16:08:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private func systemContactsForContact() -> [String] {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
return contactShare.systemContactPhoneNumbers(contactsManager)
|
|
|
|
}
|
|
|
|
|
2018-05-01 19:39:48 +02:00
|
|
|
private func updateContent() {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
guard let rootView = self.view else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("missing root view.")
|
2018-05-02 21:43:53 +02:00
|
|
|
return
|
|
|
|
}
|
2018-05-01 21:53:51 +02:00
|
|
|
|
|
|
|
for subview in rootView.subviews {
|
2018-05-01 19:39:48 +02:00
|
|
|
subview.removeFromSuperview()
|
|
|
|
}
|
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
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()
|
2018-08-07 23:29:48 +02:00
|
|
|
bottomView.backgroundColor = Theme.backgroundColor
|
2018-05-02 21:43:53 +02:00
|
|
|
self.view.addSubview(bottomView)
|
|
|
|
bottomView.layoutMargins = .zero
|
|
|
|
bottomView.autoPinWidthToSuperview()
|
|
|
|
bottomView.autoPinEdge(.top, to: .bottom, of: topView)
|
|
|
|
bottomView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
|
|
|
|
let scrollView = UIScrollView()
|
|
|
|
scrollView.preservesSuperviewLayoutMargins = false
|
|
|
|
self.view.addSubview(scrollView)
|
|
|
|
scrollView.layoutMargins = .zero
|
|
|
|
scrollView.autoPinWidthToSuperview()
|
|
|
|
scrollView.autoPinEdge(.top, to: .bottom, of: topView)
|
|
|
|
scrollView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
|
|
|
|
let fieldsView = createFieldsView()
|
|
|
|
|
|
|
|
scrollView.addSubview(fieldsView)
|
|
|
|
fieldsView.autoPinLeadingToSuperviewMargin()
|
|
|
|
fieldsView.autoPinTrailingToSuperviewMargin()
|
|
|
|
fieldsView.autoPinEdge(toSuperviewEdge: .top)
|
|
|
|
fieldsView.autoPinEdge(toSuperviewEdge: .bottom)
|
|
|
|
}
|
|
|
|
|
2018-05-04 16:30:49 +02:00
|
|
|
private func heroBackgroundColor() -> UIColor {
|
2018-08-08 15:29:23 +02:00
|
|
|
return (Theme.isDarkThemeEnabled
|
2018-08-07 23:29:48 +02:00
|
|
|
? UIColor(rgbHex: 0x272727)
|
|
|
|
: UIColor(rgbHex: 0xefeff4))
|
2018-05-04 16:30:49 +02:00
|
|
|
}
|
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
private func createTopView() -> UIView {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-02 21:43:53 +02:00
|
|
|
|
2018-05-01 19:39:48 +02:00
|
|
|
let topView = UIView.container()
|
2018-05-04 16:30:49 +02:00
|
|
|
topView.backgroundColor = heroBackgroundColor()
|
2018-05-02 21:43:53 +02:00
|
|
|
topView.preservesSuperviewLayoutMargins = false
|
|
|
|
|
|
|
|
// Back Button
|
|
|
|
let backButtonSize = CGFloat(50)
|
2018-05-25 20:59:36 +02:00
|
|
|
let backButton = TappableView(actionBlock: { [weak self] in
|
2018-05-02 21:43:53 +02:00
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
strongSelf.didPressDismiss()
|
|
|
|
})
|
|
|
|
backButton.autoSetDimension(.width, toSize: backButtonSize)
|
|
|
|
backButton.autoSetDimension(.height, toSize: backButtonSize)
|
|
|
|
topView.addSubview(backButton)
|
2018-05-14 18:19:19 +02:00
|
|
|
backButton.autoPinEdge(toSuperviewEdge: .top)
|
2018-05-02 21:43:53 +02:00
|
|
|
backButton.autoPinLeadingToSuperviewMargin()
|
|
|
|
|
2018-06-29 23:00:22 +02:00
|
|
|
let backIconName = (CurrentAppContext().isRTL ? "system_disclosure_indicator" : "system_disclosure_indicator_rtl")
|
2018-05-04 19:34:11 +02:00
|
|
|
guard let backIconImage = UIImage(named: backIconName) else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("missing icon.")
|
2018-05-04 19:34:11 +02:00
|
|
|
return topView
|
|
|
|
}
|
|
|
|
let backIconView = UIImageView(image: backIconImage.withRenderingMode(.alwaysTemplate))
|
2018-05-02 21:43:53 +02:00
|
|
|
backIconView.contentMode = .scaleAspectFit
|
2018-08-07 23:29:48 +02:00
|
|
|
backIconView.tintColor = Theme.primaryColor.withAlphaComponent(0.6)
|
2018-05-02 21:43:53 +02:00
|
|
|
backButton.addSubview(backIconView)
|
|
|
|
backIconView.autoCenterInSuperview()
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-05 04:52:43 +02:00
|
|
|
let avatarSize: CGFloat = 100
|
2018-05-02 19:25:38 +02:00
|
|
|
let avatarView = AvatarImageView()
|
2018-05-05 04:52:43 +02:00
|
|
|
avatarView.image = contactShare.getAvatarImage(diameter: avatarSize, contactsManager: contactsManager)
|
2018-05-01 19:39:48 +02:00
|
|
|
topView.addSubview(avatarView)
|
2018-05-14 18:19:19 +02:00
|
|
|
avatarView.autoPinEdge(toSuperviewEdge: .top, withInset: 20)
|
2018-05-01 19:39:48 +02:00
|
|
|
avatarView.autoHCenterInSuperview()
|
|
|
|
avatarView.autoSetDimension(.width, toSize: avatarSize)
|
|
|
|
avatarView.autoSetDimension(.height, toSize: avatarSize)
|
|
|
|
|
|
|
|
let nameLabel = UILabel()
|
2018-05-05 04:32:29 +02:00
|
|
|
nameLabel.text = contactShare.displayName
|
2018-05-04 19:06:26 +02:00
|
|
|
nameLabel.font = UIFont.ows_dynamicTypeTitle1
|
2018-08-07 23:29:48 +02:00
|
|
|
nameLabel.textColor = Theme.primaryColor
|
2018-05-01 19:39:48 +02:00
|
|
|
nameLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
nameLabel.textAlignment = .center
|
|
|
|
topView.addSubview(nameLabel)
|
|
|
|
nameLabel.autoPinEdge(.top, to: .bottom, of: avatarView, withOffset: 10)
|
2018-05-02 16:08:47 +02:00
|
|
|
nameLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin)
|
|
|
|
nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin)
|
2018-05-01 19:39:48 +02:00
|
|
|
|
|
|
|
var lastView: UIView = nameLabel
|
|
|
|
|
2018-05-04 19:06:26 +02:00
|
|
|
for phoneNumber in systemContactsWithSignalAccountsForContact() {
|
2018-05-01 19:39:48 +02:00
|
|
|
let phoneNumberLabel = UILabel()
|
2018-05-04 19:06:26 +02:00
|
|
|
phoneNumberLabel.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber)
|
|
|
|
phoneNumberLabel.font = UIFont.ows_dynamicTypeFootnote
|
2018-08-07 23:29:48 +02:00
|
|
|
phoneNumberLabel.textColor = Theme.primaryColor
|
2018-05-01 19:39:48 +02:00
|
|
|
phoneNumberLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
phoneNumberLabel.textAlignment = .center
|
|
|
|
topView.addSubview(phoneNumberLabel)
|
2018-05-01 21:53:51 +02:00
|
|
|
phoneNumberLabel.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 5)
|
2018-05-02 16:08:47 +02:00
|
|
|
phoneNumberLabel.autoPinLeadingToSuperviewMargin(withInset: hMargin)
|
|
|
|
phoneNumberLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin)
|
2018-05-01 19:39:48 +02:00
|
|
|
lastView = phoneNumberLabel
|
|
|
|
}
|
|
|
|
|
|
|
|
switch viewMode {
|
|
|
|
case .systemContactWithSignal:
|
2018-05-01 21:53:51 +02:00
|
|
|
// Show actions buttons for system contacts with a Signal account.
|
2018-05-02 16:08:47 +02:00
|
|
|
let stackView = UIStackView()
|
|
|
|
stackView.axis = .horizontal
|
|
|
|
stackView.distribution = .fillEqually
|
|
|
|
stackView.addArrangedSubview(createCircleActionButton(text: NSLocalizedString("ACTION_SEND_MESSAGE",
|
2018-07-05 23:27:37 +02:00
|
|
|
comment: "Label for 'send message' button in contact view."),
|
2018-05-08 21:52:19 +02:00
|
|
|
imageName: "contact_view_message",
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-02 16:08:47 +02:00
|
|
|
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."),
|
2018-05-08 21:52:19 +02:00
|
|
|
imageName: "contact_view_audio_call",
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-02 16:08:47 +02:00
|
|
|
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."),
|
2018-05-08 21:52:19 +02:00
|
|
|
imageName: "contact_view_video_call",
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-02 16:08:47 +02:00
|
|
|
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
|
2018-05-01 19:39:48 +02:00
|
|
|
case .systemContactWithoutSignal:
|
2018-05-01 21:53:51 +02:00
|
|
|
// Show invite button for system contacts without a Signal account.
|
2018-05-02 16:08:47 +02:00
|
|
|
let inviteButton = createLargePillButton(text: NSLocalizedString("ACTION_INVITE",
|
2018-05-04 19:06:26 +02:00
|
|
|
comment: "Label for 'invite' button in contact view."),
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-02 16:08:47 +02:00
|
|
|
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
|
2018-05-01 21:53:51 +02:00
|
|
|
case .nonSystemContact:
|
2018-05-10 19:14:30 +02:00
|
|
|
// Show no action buttons for non-system contacts.
|
2018-05-01 19:39:48 +02:00
|
|
|
break
|
|
|
|
case .noPhoneNumber:
|
2018-05-01 21:53:51 +02:00
|
|
|
// Show no action buttons for contacts without a phone number.
|
2018-05-01 19:39:48 +02:00
|
|
|
break
|
|
|
|
case .unknown:
|
2019-03-30 14:22:31 +01:00
|
|
|
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
|
2018-05-01 19:39:48 +02:00
|
|
|
topView.addSubview(activityIndicator)
|
|
|
|
activityIndicator.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 10)
|
|
|
|
activityIndicator.autoHCenterInSuperview()
|
|
|
|
lastView = activityIndicator
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2018-05-10 19:14:30 +02:00
|
|
|
// Always show "add to contacts" button.
|
|
|
|
let addToContactsButton = createLargePillButton(text: NSLocalizedString("CONVERSATION_VIEW_ADD_TO_CONTACTS_OFFER",
|
|
|
|
comment: "Message shown in conversation view that offers to add an unknown user to your phone's contacts."),
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-10 19:14:30 +02:00
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
strongSelf.didPressAddToContacts()
|
|
|
|
})
|
|
|
|
topView.addSubview(addToContactsButton)
|
|
|
|
addToContactsButton.autoPinEdge(.top, to: .bottom, of: lastView, withOffset: 20)
|
|
|
|
addToContactsButton.autoPinLeadingToSuperviewMargin(withInset: 55)
|
|
|
|
addToContactsButton.autoPinTrailingToSuperviewMargin(withInset: 55)
|
|
|
|
lastView = addToContactsButton
|
|
|
|
|
2018-05-01 21:53:51 +02:00
|
|
|
lastView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 15)
|
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
return topView
|
|
|
|
}
|
|
|
|
|
|
|
|
private func createFieldsView() -> UIView {
|
2018-08-22 19:44:22 +02:00
|
|
|
AssertIsOnMainThread()
|
2018-05-02 21:43:53 +02:00
|
|
|
|
2018-05-04 19:57:29 +02:00
|
|
|
var rows = [UIView]()
|
2018-05-01 21:53:51 +02:00
|
|
|
|
|
|
|
// 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)))
|
|
|
|
// }
|
|
|
|
|
2018-05-10 17:50:13 +02:00
|
|
|
if let organizationName = contactShare.name.organizationName?.ows_stripped() {
|
|
|
|
if (contactShare.name.hasAnyNamePart() &&
|
|
|
|
organizationName.count > 0) {
|
|
|
|
rows.append(ContactFieldView.contactFieldView(forOrganizationName: organizationName,
|
|
|
|
layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-05 04:32:29 +02:00
|
|
|
for phoneNumber in contactShare.phoneNumbers {
|
2018-05-04 23:13:42 +02:00
|
|
|
rows.append(ContactFieldView.contactFieldView(forPhoneNumber: phoneNumber,
|
|
|
|
layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin),
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-09 20:26:51 +02:00
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
strongSelf.didPressPhoneNumber(phoneNumber: phoneNumber)
|
2018-05-03 20:31:11 +02:00
|
|
|
}))
|
2018-05-01 21:53:51 +02:00
|
|
|
}
|
|
|
|
|
2018-05-05 04:32:29 +02:00
|
|
|
for email in contactShare.emails {
|
2018-05-04 23:13:42 +02:00
|
|
|
rows.append(ContactFieldView.contactFieldView(forEmail: email,
|
|
|
|
layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin),
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-09 20:26:51 +02:00
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
strongSelf.didPressEmail(email: email)
|
2018-05-03 20:31:11 +02:00
|
|
|
}))
|
2018-05-01 21:53:51 +02:00
|
|
|
}
|
|
|
|
|
2018-05-04 19:06:26 +02:00
|
|
|
for address in contactShare.addresses {
|
2018-05-04 23:13:42 +02:00
|
|
|
rows.append(ContactFieldView.contactFieldView(forAddress: address,
|
|
|
|
layoutMargins: UIEdgeInsets(top: 5, left: hMargin, bottom: 5, right: hMargin),
|
2018-05-25 20:59:36 +02:00
|
|
|
actionBlock: { [weak self] in
|
2018-05-04 23:13:42 +02:00
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
strongSelf.didPressAddress(address: address)
|
2018-05-04 19:06:26 +02:00
|
|
|
}))
|
|
|
|
}
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-04 19:57:29 +02:00
|
|
|
return ContactFieldView(rows: rows, hMargin: hMargin)
|
2018-05-01 21:53:51 +02:00
|
|
|
}
|
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
private let hMargin = CGFloat(16)
|
2018-05-01 21:53:51 +02:00
|
|
|
|
|
|
|
private func createActionRow(labelText: String, action: Selector) -> UIView {
|
|
|
|
let row = UIView()
|
2018-05-02 16:08:47 +02:00
|
|
|
row.layoutMargins.left = 0
|
|
|
|
row.layoutMargins.right = 0
|
2018-05-01 21:53:51 +02:00
|
|
|
row.isUserInteractionEnabled = true
|
|
|
|
row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: action))
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-01 21:53:51 +02:00
|
|
|
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)
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-01 21:53:51 +02:00
|
|
|
return row
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
// TODO: Use real assets.
|
2018-05-08 21:52:19 +02:00
|
|
|
private func createCircleActionButton(text: String, imageName: String, actionBlock : @escaping () -> Void) -> UIView {
|
2018-05-02 16:08:47 +02:00
|
|
|
let buttonSize = CGFloat(50)
|
2018-05-01 19:39:48 +02:00
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
let button = TappableView(actionBlock: actionBlock)
|
|
|
|
button.layoutMargins = .zero
|
|
|
|
button.autoSetDimension(.width, toSize: buttonSize, relation: .greaterThanOrEqual)
|
|
|
|
|
|
|
|
let circleView = UIView()
|
2018-08-07 23:29:48 +02:00
|
|
|
circleView.backgroundColor = Theme.backgroundColor
|
2018-05-02 16:08:47 +02:00
|
|
|
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()
|
|
|
|
|
2018-05-08 21:52:19 +02:00
|
|
|
guard let image = UIImage(named: imageName) else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("missing image.")
|
2018-05-08 21:52:19 +02:00
|
|
|
return button
|
|
|
|
}
|
2018-08-07 23:29:48 +02:00
|
|
|
let imageView = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
|
|
|
|
imageView.tintColor = Theme.primaryColor.withAlphaComponent(0.6)
|
2018-05-08 21:52:19 +02:00
|
|
|
circleView.addSubview(imageView)
|
|
|
|
imageView.autoCenterInSuperview()
|
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
let label = UILabel()
|
|
|
|
label.text = text
|
|
|
|
label.font = UIFont.ows_dynamicTypeCaption2
|
2018-08-07 23:29:48 +02:00
|
|
|
label.textColor = Theme.primaryColor
|
2018-05-02 16:08:47 +02:00
|
|
|
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)
|
2018-08-07 23:29:48 +02:00
|
|
|
button.backgroundColor = Theme.backgroundColor
|
2018-05-02 16:08:47 +02:00
|
|
|
button.layoutMargins = .zero
|
|
|
|
button.autoSetDimension(.height, toSize: 45)
|
|
|
|
button.layer.cornerRadius = 5
|
|
|
|
|
|
|
|
let label = UILabel()
|
|
|
|
label.text = text
|
2018-05-04 19:06:26 +02:00
|
|
|
label.font = UIFont.ows_dynamicTypeBody
|
2018-05-02 16:08:47 +02:00
|
|
|
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
|
|
|
|
}
|
2018-05-01 21:53:51 +02:00
|
|
|
|
|
|
|
func didPressShareContact(sender: UIGestureRecognizer) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-01 21:53:51 +02:00
|
|
|
|
|
|
|
guard sender.state == .recognized else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// TODO:
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:08:47 +02:00
|
|
|
func didPressSendMessage() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-09 00:07:25 +02:00
|
|
|
self.contactShareViewHelper.sendMessage(contactShare: self.contactShare, fromViewController: self)
|
2018-05-02 16:08:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func didPressAudioCall() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-09 00:07:25 +02:00
|
|
|
self.contactShareViewHelper.audioCall(contactShare: self.contactShare, fromViewController: self)
|
2018-05-02 16:08:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func didPressVideoCall() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-09 00:07:25 +02:00
|
|
|
self.contactShareViewHelper.videoCall(contactShare: self.contactShare, fromViewController: self)
|
2018-05-02 16:08:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func didPressInvite() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-02 16:08:47 +02:00
|
|
|
|
2018-05-09 17:13:29 +02:00
|
|
|
self.contactShareViewHelper.showInviteContact(contactShare: self.contactShare, fromViewController: self)
|
2018-05-02 16:08:47 +02:00
|
|
|
}
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-04 19:06:26 +02:00
|
|
|
func didPressAddToContacts() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-04 19:06:26 +02:00
|
|
|
|
2018-05-09 17:13:29 +02:00
|
|
|
self.contactShareViewHelper.showAddToContacts(contactShare: self.contactShare, fromViewController: self)
|
2018-05-02 20:50:05 +02:00
|
|
|
}
|
|
|
|
|
2018-05-02 21:43:53 +02:00
|
|
|
func didPressDismiss() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-02 21:43:53 +02:00
|
|
|
|
2018-05-09 16:54:55 +02:00
|
|
|
guard let navigationController = self.navigationController else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("navigationController was unexpectedly nil")
|
2018-05-09 16:54:55 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
navigationController.popViewController(animated: true)
|
2018-05-02 21:43:53 +02:00
|
|
|
}
|
|
|
|
|
2018-05-09 20:26:51 +02:00
|
|
|
func didPressPhoneNumber(phoneNumber: OWSContactPhoneNumber) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
|
|
|
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
|
|
|
|
|
|
if let e164 = phoneNumber.tryToConvertToE164() {
|
|
|
|
if contactShare.systemContactsWithSignalAccountPhoneNumbers(contactsManager).contains(e164) {
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_SEND_MESSAGE",
|
2018-07-05 23:27:37 +02:00
|
|
|
comment: "Label for 'send message' button in contact view."),
|
2018-05-09 20:26:51 +02:00
|
|
|
style: .default) { _ in
|
Faster conversation presentation.
There are multiple places in the codebase we present a conversation.
We used to have some very conservative machinery around how this was done, for
fear of failing to present the call view controller, which would have left a
hidden call in the background. We've since addressed that concern more
thoroughly via the separate calling UIWindow.
As such, the remaining presentation machinery is overly complex and inflexible
for what we need.
Sometimes we want to animate-push the conversation. (tap on home, tap on "send message" in contact card/group members)
Sometimes we want to dismiss a modal, to reveal the conversation behind it (contact picker, group creation)
Sometimes we want to present the conversation with no animation (becoming active from a notification)
We also want to ensure that we're never pushing more than one conversation view
controller, which was previously a problem since we were "pushing" a newly
constructed VC in response to these myriad actions. It turned out there were
certain code paths that caused multiple actions to be fired in rapid succession
which pushed multiple ConversationVC's.
The built-in method: `setViewControllers:animated` easily ensures we only have
one ConversationVC on the stack, while being composable enough to faciliate the
various more efficient animations we desire.
The only thing lost with the complex methods is that the naive
`presentViewController:` can fail, e.g. if another view is already presented.
E.g. if an alert appears *just* before the user taps compose, the contact
picker will fail to present.
Since we no longer depend on this for presenting the CallViewController, this
isn't catostrophic, and in fact, arguable preferable, since we want the user to
read and dismiss any alert explicitly.
// FREEBIE
2018-08-18 22:54:35 +02:00
|
|
|
SignalApp.shared().presentConversation(forRecipientId: e164, action: .compose, animated: true)
|
2018-05-09 20:26:51 +02:00
|
|
|
})
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_AUDIO_CALL",
|
|
|
|
comment: "Label for 'audio call' button in contact view."),
|
|
|
|
style: .default) { _ in
|
Faster conversation presentation.
There are multiple places in the codebase we present a conversation.
We used to have some very conservative machinery around how this was done, for
fear of failing to present the call view controller, which would have left a
hidden call in the background. We've since addressed that concern more
thoroughly via the separate calling UIWindow.
As such, the remaining presentation machinery is overly complex and inflexible
for what we need.
Sometimes we want to animate-push the conversation. (tap on home, tap on "send message" in contact card/group members)
Sometimes we want to dismiss a modal, to reveal the conversation behind it (contact picker, group creation)
Sometimes we want to present the conversation with no animation (becoming active from a notification)
We also want to ensure that we're never pushing more than one conversation view
controller, which was previously a problem since we were "pushing" a newly
constructed VC in response to these myriad actions. It turned out there were
certain code paths that caused multiple actions to be fired in rapid succession
which pushed multiple ConversationVC's.
The built-in method: `setViewControllers:animated` easily ensures we only have
one ConversationVC on the stack, while being composable enough to faciliate the
various more efficient animations we desire.
The only thing lost with the complex methods is that the naive
`presentViewController:` can fail, e.g. if another view is already presented.
E.g. if an alert appears *just* before the user taps compose, the contact
picker will fail to present.
Since we no longer depend on this for presenting the CallViewController, this
isn't catostrophic, and in fact, arguable preferable, since we want the user to
read and dismiss any alert explicitly.
// FREEBIE
2018-08-18 22:54:35 +02:00
|
|
|
SignalApp.shared().presentConversation(forRecipientId: e164, action: .audioCall, animated: true)
|
2018-05-09 20:26:51 +02:00
|
|
|
})
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ACTION_VIDEO_CALL",
|
|
|
|
comment: "Label for 'video call' button in contact view."),
|
|
|
|
style: .default) { _ in
|
Faster conversation presentation.
There are multiple places in the codebase we present a conversation.
We used to have some very conservative machinery around how this was done, for
fear of failing to present the call view controller, which would have left a
hidden call in the background. We've since addressed that concern more
thoroughly via the separate calling UIWindow.
As such, the remaining presentation machinery is overly complex and inflexible
for what we need.
Sometimes we want to animate-push the conversation. (tap on home, tap on "send message" in contact card/group members)
Sometimes we want to dismiss a modal, to reveal the conversation behind it (contact picker, group creation)
Sometimes we want to present the conversation with no animation (becoming active from a notification)
We also want to ensure that we're never pushing more than one conversation view
controller, which was previously a problem since we were "pushing" a newly
constructed VC in response to these myriad actions. It turned out there were
certain code paths that caused multiple actions to be fired in rapid succession
which pushed multiple ConversationVC's.
The built-in method: `setViewControllers:animated` easily ensures we only have
one ConversationVC on the stack, while being composable enough to faciliate the
various more efficient animations we desire.
The only thing lost with the complex methods is that the naive
`presentViewController:` can fail, e.g. if another view is already presented.
E.g. if an alert appears *just* before the user taps compose, the contact
picker will fail to present.
Since we no longer depend on this for presenting the CallViewController, this
isn't catostrophic, and in fact, arguable preferable, since we want the user to
read and dismiss any alert explicitly.
// FREEBIE
2018-08-18 22:54:35 +02:00
|
|
|
SignalApp.shared().presentConversation(forRecipientId: e164, action: .videoCall, animated: true)
|
2018-05-09 20:26:51 +02:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// TODO: We could offer callPhoneNumberWithSystemCall.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION",
|
|
|
|
comment: "Short name for edit menu item to copy contents of media message."),
|
|
|
|
style: .default) { _ in
|
|
|
|
UIPasteboard.general.string = phoneNumber.phoneNumber
|
|
|
|
})
|
|
|
|
actionSheet.addAction(OWSAlerts.cancelAction)
|
2019-03-21 15:55:04 +01:00
|
|
|
presentAlert(actionSheet)
|
2018-05-09 20:26:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func callPhoneNumberWithSystemCall(phoneNumber: OWSContactPhoneNumber) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
|
|
|
guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("could not open phone number.")
|
2018-05-09 20:26:51 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
UIApplication.shared.openURL(url as URL)
|
|
|
|
}
|
|
|
|
|
|
|
|
func didPressEmail(email: OWSContactEmail) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
|
|
|
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
2018-05-09 20:36:05 +02:00
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONTACT_VIEW_OPEN_EMAIL_IN_EMAIL_APP",
|
2018-05-09 20:26:51 +02:00
|
|
|
comment: "Label for 'open email in email app' button in contact view."),
|
|
|
|
style: .default) { [weak self] _ in
|
|
|
|
self?.openEmailInEmailApp(email: email)
|
|
|
|
})
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION",
|
|
|
|
comment: "Short name for edit menu item to copy contents of media message."),
|
|
|
|
style: .default) { _ in
|
|
|
|
UIPasteboard.general.string = email.email
|
|
|
|
})
|
|
|
|
actionSheet.addAction(OWSAlerts.cancelAction)
|
2019-03-21 15:55:04 +01:00
|
|
|
presentAlert(actionSheet)
|
2018-05-09 20:26:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func openEmailInEmailApp(email: OWSContactEmail) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
|
|
|
guard let url = NSURL(string: "mailto:\(email.email)") else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("could not open email.")
|
2018-05-09 20:26:51 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
UIApplication.shared.openURL(url as URL)
|
|
|
|
}
|
|
|
|
|
2018-05-04 19:06:26 +02:00
|
|
|
func didPressAddress(address: OWSContactAddress) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-04 19:06:26 +02:00
|
|
|
|
2018-05-09 20:26:51 +02:00
|
|
|
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("CONTACT_VIEW_OPEN_ADDRESS_IN_MAPS_APP",
|
|
|
|
comment: "Label for 'open address in maps app' button in contact view."),
|
|
|
|
style: .default) { [weak self] _ in
|
|
|
|
self?.openAddressInMaps(address: address)
|
|
|
|
})
|
|
|
|
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("EDIT_ITEM_COPY_ACTION",
|
|
|
|
comment: "Short name for edit menu item to copy contents of media message."),
|
|
|
|
style: .default) { [weak self] _ in
|
|
|
|
guard let strongSelf = self else { return }
|
|
|
|
|
|
|
|
UIPasteboard.general.string = strongSelf.formatAddressForQuery(address: address)
|
|
|
|
})
|
|
|
|
actionSheet.addAction(OWSAlerts.cancelAction)
|
2019-03-21 15:55:04 +01:00
|
|
|
presentAlert(actionSheet)
|
2018-05-09 20:26:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func openAddressInMaps(address: OWSContactAddress) {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
|
|
|
let mapAddress = formatAddressForQuery(address: address)
|
|
|
|
guard let escapedMapAddress = mapAddress.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("could not open address.")
|
2018-05-09 20:26:51 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// Note that we use "q" (i.e. query) rather than "address" since we can't assume
|
|
|
|
// this is a well-formed address.
|
|
|
|
guard let url = URL(string: "http://maps.apple.com/?q=\(escapedMapAddress)") else {
|
2018-08-27 16:27:48 +02:00
|
|
|
owsFailDebug("could not open address.")
|
2018-05-09 20:26:51 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
UIApplication.shared.openURL(url as URL)
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatAddressForQuery(address: OWSContactAddress) -> String {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-09 20:26:51 +02:00
|
|
|
|
2018-05-04 19:06:26 +02:00
|
|
|
// Open address in Apple Maps app.
|
|
|
|
var addressParts = [String]()
|
|
|
|
let addAddressPart: ((String?) -> Void) = { (part) in
|
|
|
|
guard let part = part else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
guard part.count > 0 else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
addressParts.append(part)
|
|
|
|
}
|
|
|
|
addAddressPart(address.street)
|
|
|
|
addAddressPart(address.neighborhood)
|
|
|
|
addAddressPart(address.city)
|
|
|
|
addAddressPart(address.region)
|
|
|
|
addAddressPart(address.postcode)
|
|
|
|
addAddressPart(address.country)
|
2018-05-09 20:26:51 +02:00
|
|
|
return addressParts.joined(separator: ", ")
|
2018-05-04 19:06:26 +02:00
|
|
|
}
|
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
// MARK: - ContactShareViewHelperDelegate
|
2018-05-01 21:53:51 +02:00
|
|
|
|
2018-05-07 21:20:34 +02:00
|
|
|
public func didCreateOrEditContact() {
|
2018-08-23 16:37:34 +02:00
|
|
|
Logger.info("")
|
2018-05-01 21:53:51 +02:00
|
|
|
updateContent()
|
2018-05-15 00:25:17 +02:00
|
|
|
|
|
|
|
self.dismiss(animated: true)
|
2018-05-01 21:53:51 +02:00
|
|
|
}
|
2018-05-01 19:39:48 +02:00
|
|
|
}
|