Merge branch 'charlesmchen/contactShareDesign'
This commit is contained in:
commit
8622dba39f
@ -77,7 +77,6 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
required init(contact: OWSContact) {
|
||||
contactsManager = Environment.current().contactsManager
|
||||
self.contact = contact
|
||||
self.scrollView = UIScrollView()
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
@ -98,15 +97,16 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
UIUtil.applySignalAppearence()
|
||||
|
||||
if let navigationController = self.navigationController {
|
||||
owsFail("\(logTag) missing navigationController")
|
||||
navigationController.isNavigationBarHidden = true
|
||||
}
|
||||
|
||||
contactsManager.requestSystemContactsOnce(completion: { [weak self] _ in
|
||||
guard let strongSelf = self else { return }
|
||||
strongSelf.updateMode()
|
||||
@ -119,18 +119,24 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
UIUtil.applySignalAppearence()
|
||||
}
|
||||
|
||||
private var scrollView: UIScrollView
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if let navigationController = self.navigationController {
|
||||
owsFail("\(logTag) missing navigationController")
|
||||
navigationController.isNavigationBarHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
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.preservesSuperviewLayoutMargins = false
|
||||
self.view.backgroundColor = heroBackgroundColor()
|
||||
|
||||
updateContent()
|
||||
|
||||
@ -185,26 +191,90 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
private func updateContent() {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
let rootView = self.scrollView
|
||||
guard let rootView = self.view else {
|
||||
owsFail("\(logTag) missing root view.")
|
||||
return
|
||||
}
|
||||
|
||||
for subview in rootView.subviews {
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
|
||||
// TODO: The design calls for no navigation bar, just a back button.
|
||||
let topView = UIView.container()
|
||||
topView.backgroundColor = UIColor(rgbHex: 0xefeff4)
|
||||
topView.preservesSuperviewLayoutMargins = true
|
||||
let topView = createTopView()
|
||||
rootView.addSubview(topView)
|
||||
topView.autoPinEdge(toSuperviewEdge: .top)
|
||||
topView.autoPinEdge(.left, to: .left, of: self.view)
|
||||
topView.autoPinEdge(.right, to: .right, of: self.view)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
private func heroBackgroundColor() -> UIColor {
|
||||
return UIColor(rgbHex: 0xefeff4)
|
||||
}
|
||||
|
||||
private func createTopView() -> UIView {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
let topView = UIView.container()
|
||||
topView.backgroundColor = heroBackgroundColor()
|
||||
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.autoPinLeadingToSuperviewMargin()
|
||||
|
||||
let backIconName = (self.view.isRTL() ? "system_disclosure_indicator" : "system_disclosure_indicator_rtl")
|
||||
guard let backIconImage = UIImage(named: backIconName) else {
|
||||
owsFail("\(logTag) missing icon.")
|
||||
return topView
|
||||
}
|
||||
let backIconView = UIImageView(image: backIconImage.withRenderingMode(.alwaysTemplate))
|
||||
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 = UIView.container()
|
||||
avatarView.backgroundColor = UIColor.ows_materialBlue
|
||||
avatarView.layer.cornerRadius = avatarSize * 0.5
|
||||
|
||||
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()
|
||||
@ -226,7 +296,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
if let firstPhoneNumber = contact.phoneNumbers.first {
|
||||
let phoneNumberLabel = UILabel()
|
||||
phoneNumberLabel.text = firstPhoneNumber.phoneNumber
|
||||
phoneNumberLabel.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: firstPhoneNumber.phoneNumber)
|
||||
phoneNumberLabel.font = UIFont.ows_dynamicTypeCaption2
|
||||
phoneNumberLabel.textColor = UIColor.black
|
||||
phoneNumberLabel.lineBreakMode = .byTruncatingTail
|
||||
@ -297,16 +367,15 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
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)
|
||||
bottomView.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
bottomView.autoPinEdge(.left, to: .left, of: self.view)
|
||||
bottomView.autoPinEdge(.right, to: .right, of: self.view)
|
||||
bottomView.setContentHuggingVerticalLow()
|
||||
return topView
|
||||
}
|
||||
|
||||
private func createFieldsView() -> UIView {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
let fieldsView = UIView.container()
|
||||
fieldsView.layoutMargins = .zero
|
||||
fieldsView.preservesSuperviewLayoutMargins = false
|
||||
|
||||
var lastRow: UIView?
|
||||
|
||||
@ -317,7 +386,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
}
|
||||
let row = UIView()
|
||||
row.backgroundColor = UIColor(rgbHex: 0xdedee1)
|
||||
bottomView.addSubview(row)
|
||||
fieldsView.addSubview(row)
|
||||
row.autoSetDimension(.height, toSize: 1)
|
||||
row.autoPinLeadingToSuperviewMargin(withInset: self.hMargin)
|
||||
row.autoPinTrailingToSuperviewMargin()
|
||||
@ -329,7 +398,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
if lastRow != nil {
|
||||
addSpacerRow()
|
||||
}
|
||||
bottomView.addSubview(row)
|
||||
fieldsView.addSubview(row)
|
||||
row.autoPinLeadingToSuperviewMargin()
|
||||
row.autoPinTrailingToSuperviewMargin()
|
||||
if let lastRow = lastRow {
|
||||
@ -359,9 +428,10 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
// }
|
||||
|
||||
for phoneNumber in contact.phoneNumbers {
|
||||
// TODO: Try to format the phone number nicely.
|
||||
let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber)
|
||||
|
||||
addRow(createNameValueRow(name: phoneNumber.localizedLabel(),
|
||||
value: phoneNumber.phoneNumber,
|
||||
value: formattedPhoneNumber,
|
||||
actionBlock: {
|
||||
guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else {
|
||||
owsFail("\(ContactViewController.logTag) could not open phone number.")
|
||||
@ -386,6 +456,8 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
// TODO: Should we present addresses here too? How?
|
||||
|
||||
lastRow?.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
|
||||
return fieldsView
|
||||
}
|
||||
|
||||
private let hMargin = CGFloat(16)
|
||||
@ -411,7 +483,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
return row
|
||||
}
|
||||
|
||||
private func createNameValueRow(name: String, value: String, actionBlock : @escaping () -> Void) -> UIView {
|
||||
private func createNameValueRow(name: String, value: String?, actionBlock : @escaping () -> Void) -> UIView {
|
||||
let row = TappableView(actionBlock: actionBlock)
|
||||
row.layoutMargins.left = 0
|
||||
row.layoutMargins.right = 0
|
||||
@ -527,40 +599,38 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
func didPressSendMessage() {
|
||||
Logger.info("\(logTag) \(#function)")
|
||||
|
||||
// TODO: We're taking the first Signal account id. We might
|
||||
// want to let the user select if there's more than one.
|
||||
guard let recipientId = systemContactsWithSignalAccountsForContact().first else {
|
||||
owsFail("\(logTag) missing Signal recipient id.")
|
||||
return
|
||||
}
|
||||
|
||||
SignalApp.shared().presentConversation(forRecipientId: recipientId, action: .compose)
|
||||
presentThreadAndPeform(action: .compose)
|
||||
}
|
||||
|
||||
func didPressAudioCall() {
|
||||
Logger.info("\(logTag) \(#function)")
|
||||
|
||||
// TODO: We're taking the first Signal account id. We might
|
||||
// want to let the user select if there's more than one.
|
||||
guard let recipientId = systemContactsWithSignalAccountsForContact().first else {
|
||||
owsFail("\(logTag) missing Signal recipient id.")
|
||||
return
|
||||
}
|
||||
|
||||
SignalApp.shared().presentConversation(forRecipientId: recipientId, action: .audioCall)
|
||||
presentThreadAndPeform(action: .audioCall)
|
||||
}
|
||||
|
||||
func didPressVideoCall() {
|
||||
Logger.info("\(logTag) \(#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.
|
||||
guard let recipientId = systemContactsWithSignalAccountsForContact().first else {
|
||||
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
|
||||
}
|
||||
|
||||
SignalApp.shared().presentConversation(forRecipientId: recipientId, action: .videoCall)
|
||||
showPhoneNumberPicker(phoneNumbers: phoneNumbers, completion: { (recipientId) in
|
||||
SignalApp.shared().presentConversation(forRecipientId: recipientId, action: action)
|
||||
})
|
||||
}
|
||||
|
||||
func didPressInvite() {
|
||||
@ -582,6 +652,27 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
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.bestEffortLocalizedPhoneNumber(withE164: phoneNumber),
|
||||
style: .default) { _ in
|
||||
completion(phoneNumber)
|
||||
})
|
||||
}
|
||||
actionSheet.addAction(OWSAlerts.cancelAction)
|
||||
|
||||
self.present(actionSheet, animated: true)
|
||||
}
|
||||
|
||||
func didPressDismiss() {
|
||||
Logger.info("\(self.logTag) \(#function)")
|
||||
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private func presentNewContactView() {
|
||||
|
||||
@ -3,10 +3,14 @@
|
||||
//
|
||||
|
||||
#import "OWSContactShareView.h"
|
||||
#import "OWSContactAvatarBuilder.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "UIColor+JSQMessages.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
#import <SignalMessaging/Environment.h>
|
||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||
#import <SignalServiceKit/OWSContact.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@ -101,22 +105,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin];
|
||||
|
||||
UIView *iconCircleView = [UIView containerView];
|
||||
iconCircleView.backgroundColor = [UIColor colorWithRGBHex:0x00ffff];
|
||||
iconCircleView.layer.cornerRadius = self.iconSize * 0.5f;
|
||||
[iconCircleView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[iconCircleView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
[iconCircleView setCompressionResistanceHigh];
|
||||
[iconCircleView setContentHuggingHigh];
|
||||
|
||||
// TODO: Use avatar, if present and downloaded. else default.
|
||||
UIImage *image = [UIImage imageNamed:@"attachment_file"];
|
||||
OWSAssert(image);
|
||||
UIImageView *imageView = [UIImageView new];
|
||||
imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
imageView.tintColor = self.bubbleBackgroundColor;
|
||||
[iconCircleView addSubview:imageView];
|
||||
[imageView autoCenterInSuperview];
|
||||
// TODO: Use the contact's avatar if present and downloaded.
|
||||
AvatarImageView *avatarView = [AvatarImageView new];
|
||||
// TODO: What's the best colorSeed value to use?
|
||||
OWSAvatarBuilder *avatarBuilder =
|
||||
[[OWSContactAvatarBuilder alloc] initWithNonSignalName:self.contactShare.displayName
|
||||
colorSeed:self.contactShare.displayName
|
||||
diameter:(NSUInteger)self.iconSize
|
||||
contactsManager:[Environment current].contactsManager];
|
||||
avatarView.image = [avatarBuilder build];
|
||||
[avatarView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[avatarView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
[avatarView setCompressionResistanceHigh];
|
||||
[avatarView setContentHuggingHigh];
|
||||
|
||||
UILabel *topLabel = [UILabel new];
|
||||
topLabel.text = self.contactShare.displayName;
|
||||
@ -135,7 +136,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
NSString *_Nullable firstPhoneNumber = self.contactShare.phoneNumbers.firstObject.phoneNumber;
|
||||
if (firstPhoneNumber.length > 0) {
|
||||
UILabel *bottomLabel = [UILabel new];
|
||||
bottomLabel.text = firstPhoneNumber;
|
||||
bottomLabel.text = [PhoneNumber bestEffortLocalizedPhoneNumberWithE164:firstPhoneNumber];
|
||||
// TODO:
|
||||
bottomLabel.textColor = [UIColor ows_darkGrayColor];
|
||||
bottomLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
@ -166,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
|
||||
[stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
|
||||
|
||||
[stackView addArrangedSubview:iconCircleView];
|
||||
[stackView addArrangedSubview:avatarView];
|
||||
[stackView addArrangedSubview:labelsView];
|
||||
[stackView addArrangedSubview:disclosureImageView];
|
||||
}
|
||||
|
||||
@ -3000,17 +3000,14 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
phoneNumber2,
|
||||
];
|
||||
|
||||
OWSContactEmail *email1 = [OWSContactEmail new];
|
||||
email1.emailType = OWSContactEmailType_Home;
|
||||
email1.email = @"a1@b.com";
|
||||
OWSContactEmail *email2 = [OWSContactEmail new];
|
||||
email2.emailType = OWSContactEmailType_Custom;
|
||||
email2.label = @"customer support";
|
||||
email2.email = @"a2@b.com";
|
||||
contact.emails = @[
|
||||
email1,
|
||||
email2,
|
||||
];
|
||||
NSMutableArray<OWSContactEmail *> *emails = [NSMutableArray new];
|
||||
for (NSUInteger i = 0; i < 16; i++) {
|
||||
OWSContactEmail *email = [OWSContactEmail new];
|
||||
email.emailType = OWSContactEmailType_Home;
|
||||
email.email = [NSString stringWithFormat:@"a%zd@b.com", i];
|
||||
[emails addObject:email];
|
||||
}
|
||||
contact.emails = emails;
|
||||
|
||||
OWSContactAddress *address1 = [OWSContactAddress new];
|
||||
address1.addressType = OWSContactAddressType_Home;
|
||||
@ -3178,17 +3175,14 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
phoneNumber2,
|
||||
];
|
||||
|
||||
OWSContactEmail *email1 = [OWSContactEmail new];
|
||||
email1.emailType = OWSContactEmailType_Home;
|
||||
email1.email = @"a@b.com";
|
||||
OWSContactEmail *email2 = [OWSContactEmail new];
|
||||
email2.emailType = OWSContactEmailType_Custom;
|
||||
email2.label = @"customer support";
|
||||
email2.email = @"a@b.com";
|
||||
contact.emails = @[
|
||||
email1,
|
||||
email2,
|
||||
];
|
||||
NSMutableArray<OWSContactEmail *> *emails = [NSMutableArray new];
|
||||
for (NSUInteger i = 0; i < 16; i++) {
|
||||
OWSContactEmail *email = [OWSContactEmail new];
|
||||
email.emailType = OWSContactEmailType_Home;
|
||||
email.email = [NSString stringWithFormat:@"a%zd@b.com", i];
|
||||
[emails addObject:email];
|
||||
}
|
||||
contact.emails = emails;
|
||||
|
||||
OWSContactAddress *address1 = [OWSContactAddress new];
|
||||
address1.addressType = OWSContactAddressType_Home;
|
||||
|
||||
@ -110,8 +110,11 @@ class ReminderView: UIView {
|
||||
|
||||
// Icon
|
||||
let iconName = (self.isRTL() ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator")
|
||||
let iconImage = UIImage(named: iconName)?.withRenderingMode(.alwaysTemplate)
|
||||
let iconView = UIImageView(image: iconImage)
|
||||
guard let iconImage = UIImage(named: iconName) else {
|
||||
owsFail("\(logTag) missing icon.")
|
||||
return
|
||||
}
|
||||
let iconView = UIImageView(image: iconImage.withRenderingMode(.alwaysTemplate))
|
||||
iconView.contentMode = .scaleAspectFit
|
||||
iconView.tintColor = UIColor.black.withAlphaComponent(0.6)
|
||||
container.addSubview(iconView)
|
||||
|
||||
@ -406,6 +406,9 @@
|
||||
/* Generic button text to proceed with an action */
|
||||
"CONFIRMATION_TITLE" = "Confirm";
|
||||
|
||||
/* Label for a contact's postal address. */
|
||||
"CONTACT_ADDRESS" = "Address";
|
||||
|
||||
/* An indicator that a contact has been blocked. */
|
||||
"CONTACT_CELL_IS_BLOCKED" = "Blocked";
|
||||
|
||||
@ -415,6 +418,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"CONTACT_DETAIL_COMM_TYPE_INSECURE" = "Unregistered Number";
|
||||
|
||||
/* Label for a contact's email address. */
|
||||
"CONTACT_EMAIL" = "Email";
|
||||
|
||||
/* Label for a contact's phone number. */
|
||||
"CONTACT_PHONE" = "Phone";
|
||||
|
||||
/* table cell subtitle when contact card has no email */
|
||||
"CONTACT_PICKER_NO_EMAILS_AVAILABLE" = "No email available.";
|
||||
|
||||
@ -424,6 +433,9 @@
|
||||
/* navbar title for contact picker when sharing a contact */
|
||||
"CONTACT_PICKER_TITLE" = "Select Contact";
|
||||
|
||||
/* Indicates that a contact has no name. */
|
||||
"CONTACT_WITHOUT_NAME" = "Unnamed Contact";
|
||||
|
||||
/* title for conversation settings screen */
|
||||
"CONVERSATION_SETTINGS" = "Conversation Settings";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#define COUNTRY_CODE_PREFIX @"+"
|
||||
@ -30,6 +30,7 @@
|
||||
+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input;
|
||||
+ (NSString *)bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:(NSString *)input
|
||||
withSpecifiedCountryCodeString:(NSString *)countryCodeString;
|
||||
+ (NSString *)bestEffortLocalizedPhoneNumberWithE164:(NSString *)phoneNumber;
|
||||
|
||||
+ (NSString *)regionCodeFromCountryCodeString:(NSString *)countryCodeString;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PhoneNumber.h"
|
||||
@ -119,6 +119,45 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSString *)formatIntAsEN:(int)value
|
||||
{
|
||||
static NSNumberFormatter *formatter = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [NSNumberFormatter new];
|
||||
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
|
||||
});
|
||||
return [formatter stringFromNumber:@(value)];
|
||||
}
|
||||
|
||||
+ (NSString *)bestEffortLocalizedPhoneNumberWithE164:(NSString *)phoneNumber
|
||||
{
|
||||
OWSAssert(phoneNumber);
|
||||
|
||||
PhoneNumber *_Nullable parsedPhoneNumber = [self tryParsePhoneNumberFromE164:phoneNumber];
|
||||
if (!parsedPhoneNumber) {
|
||||
DDLogWarn(@"%@ could not parse phone number.", self.logTag);
|
||||
return phoneNumber;
|
||||
}
|
||||
NSNumber *_Nullable countryCode = [parsedPhoneNumber getCountryCode];
|
||||
if (!countryCode) {
|
||||
DDLogWarn(@"%@ parsed phone number has no country code.", self.logTag);
|
||||
return phoneNumber;
|
||||
}
|
||||
NSString *countryCodeString = [self formatIntAsEN:countryCode.intValue];
|
||||
if (countryCodeString.length < 1) {
|
||||
DDLogWarn(@"%@ invalid country code.", self.logTag);
|
||||
return phoneNumber;
|
||||
}
|
||||
NSString *_Nullable formattedPhoneNumber =
|
||||
[self bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:phoneNumber
|
||||
withSpecifiedRegionCode:countryCodeString];
|
||||
if (!countryCode) {
|
||||
DDLogWarn(@"%@ could not format phone number.", self.logTag);
|
||||
return phoneNumber;
|
||||
}
|
||||
return formattedPhoneNumber;
|
||||
}
|
||||
|
||||
+ (NSString *)regionCodeFromCountryCodeString:(NSString *)countryCodeString {
|
||||
NBPhoneNumberUtil *phoneUtil = [PhoneNumberUtil sharedUtil].nbPhoneNumberUtil;
|
||||
|
||||
@ -55,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, nullable) NSString *namePrefix;
|
||||
@property (nonatomic, nullable) NSString *middleName;
|
||||
@property (nonatomic, nullable) NSString *organizationName;
|
||||
@property (nonatomic, nullable) NSString *displayName;
|
||||
@property (nonatomic) NSString *displayName;
|
||||
|
||||
@property (nonatomic) NSArray<OWSContactPhoneNumber *> *phoneNumbers;
|
||||
@property (nonatomic) NSArray<OWSContactEmail *> *emails;
|
||||
|
||||
@ -109,7 +109,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
|
||||
@property (nonatomic, readonly, nullable) NSString *namePrefix;
|
||||
@property (nonatomic, readonly, nullable) NSString *middleName;
|
||||
@property (nonatomic, readonly, nullable) NSString *organizationName;
|
||||
@property (nonatomic, readonly, nullable) NSString *displayName;
|
||||
@property (nonatomic, readonly) NSString *displayName;
|
||||
|
||||
@property (nonatomic, readonly) NSArray<OWSContactPhoneNumber *> *phoneNumbers;
|
||||
@property (nonatomic, readonly) NSArray<OWSContactEmail *> *emails;
|
||||
|
||||
@ -49,18 +49,7 @@ NSString *NSStringForContactPhoneType(OWSContactPhoneType value)
|
||||
DDLogWarn(@"%@ invalid phone number; not e164: %@.", self.logTag, self.phoneNumber);
|
||||
return NO;
|
||||
}
|
||||
switch (self.phoneType) {
|
||||
case OWSContactPhoneType_Home:
|
||||
case OWSContactPhoneType_Mobile:
|
||||
case OWSContactPhoneType_Work:
|
||||
return YES;
|
||||
case OWSContactPhoneType_Custom:
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
DDLogWarn(@"%@ invalid phone number; missing custom label: %@.", self.logTag, self.label);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)localizedLabel
|
||||
@ -73,7 +62,10 @@ NSString *NSStringForContactPhoneType(OWSContactPhoneType value)
|
||||
case OWSContactPhoneType_Work:
|
||||
return [CNLabeledValue localizedStringForLabel:CNLabelWork];
|
||||
default:
|
||||
return self.label;
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
return NSLocalizedString(@"CONTACT_PHONE", @"Label for a contact's phone number.");
|
||||
}
|
||||
return self.label.ows_stripped;
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,18 +122,7 @@ NSString *NSStringForContactEmailType(OWSContactEmailType value)
|
||||
DDLogWarn(@"%@ invalid email: %@.", self.logTag, self.email);
|
||||
return NO;
|
||||
}
|
||||
switch (self.emailType) {
|
||||
case OWSContactEmailType_Home:
|
||||
case OWSContactEmailType_Mobile:
|
||||
case OWSContactEmailType_Work:
|
||||
return YES;
|
||||
case OWSContactEmailType_Custom:
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
DDLogWarn(@"%@ invalid email; missing custom label: %@.", self.logTag, self.label);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)localizedLabel
|
||||
@ -154,7 +135,10 @@ NSString *NSStringForContactEmailType(OWSContactEmailType value)
|
||||
case OWSContactEmailType_Work:
|
||||
return [CNLabeledValue localizedStringForLabel:CNLabelWork];
|
||||
default:
|
||||
return self.label;
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
return NSLocalizedString(@"CONTACT_EMAIL", @"Label for a contact's email address.");
|
||||
}
|
||||
return self.label.ows_stripped;
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,17 +201,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
DDLogWarn(@"%@ invalid address; empty.", self.logTag);
|
||||
return NO;
|
||||
}
|
||||
switch (self.addressType) {
|
||||
case OWSContactAddressType_Home:
|
||||
case OWSContactAddressType_Work:
|
||||
return YES;
|
||||
case OWSContactAddressType_Custom:
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
DDLogWarn(@"%@ invalid address; missing custom label: %@.", self.logTag, self.label);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)localizedLabel
|
||||
@ -238,7 +212,10 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
case OWSContactAddressType_Work:
|
||||
return [CNLabeledValue localizedStringForLabel:CNLabelWork];
|
||||
default:
|
||||
return self.label;
|
||||
if (self.label.ows_stripped.length < 1) {
|
||||
return NSLocalizedString(@"CONTACT_ADDRESS", @"Label for a contact's postal address.");
|
||||
}
|
||||
return self.label.ows_stripped;
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +265,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
@property (nonatomic, nullable) NSString *namePrefix;
|
||||
@property (nonatomic, nullable) NSString *middleName;
|
||||
@property (nonatomic, nullable) NSString *organizationName;
|
||||
@property (nonatomic, nullable) NSString *displayName;
|
||||
@property (nonatomic) NSString *displayName;
|
||||
|
||||
@property (nonatomic) NSArray<OWSContactPhoneNumber *> *phoneNumbers;
|
||||
@property (nonatomic) NSArray<OWSContactEmail *> *emails;
|
||||
@ -360,10 +337,14 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
return hasValue;
|
||||
}
|
||||
|
||||
- (nullable NSString *)displayName
|
||||
- (NSString *)displayName
|
||||
{
|
||||
[self ensureDisplayName];
|
||||
|
||||
if (_displayName.length < 1) {
|
||||
OWSProdLogAndFail(@"%@ could not derive a valid display name.", self.logTag);
|
||||
return NSLocalizedString(@"CONTACT_WITHOUT_NAME", @"Indicates that a contact has no name.");
|
||||
}
|
||||
return _displayName;
|
||||
}
|
||||
|
||||
@ -377,9 +358,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
// Fall back to using the organization name.
|
||||
_displayName = self.organizationName;
|
||||
}
|
||||
if (_displayName.length < 1) {
|
||||
OWSProdLogAndFail(@"%@ could not derive a valid display name.", self.logTag);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription
|
||||
|
||||
Loading…
Reference in New Issue
Block a user