diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 144b01541..6aad516aa 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 34330A5E1E787BD800DF2FB9 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */; }; 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; }; 34382266209A4E400094FEB7 /* ApproveContactShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34382265209A4E3F0094FEB7 /* ApproveContactShareViewController.swift */; }; + 3438226A209B63500094FEB7 /* EditContactShareNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34382269209B63500094FEB7 /* EditContactShareNameViewController.swift */; }; 34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34386A4D207D0C01009F5D9C /* HomeViewController.m */; }; 34386A52207D0C01009F5D9C /* HomeViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34386A50207D0C01009F5D9C /* HomeViewCell.m */; }; 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34386A53207D271C009F5D9C /* NeverClearView.swift */; }; @@ -632,6 +633,7 @@ 34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = ""; }; 34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = ""; }; 34382265209A4E3F0094FEB7 /* ApproveContactShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApproveContactShareViewController.swift; path = SignalMessaging/attachments/ApproveContactShareViewController.swift; sourceTree = SOURCE_ROOT; }; + 34382269209B63500094FEB7 /* EditContactShareNameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditContactShareNameViewController.swift; path = SignalMessaging/attachments/EditContactShareNameViewController.swift; sourceTree = SOURCE_ROOT; }; 34386A4D207D0C01009F5D9C /* HomeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeViewController.m; sourceTree = ""; }; 34386A4E207D0C01009F5D9C /* HomeViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewCell.h; sourceTree = ""; }; 34386A4F207D0C01009F5D9C /* HomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewController.h; sourceTree = ""; }; @@ -1602,6 +1604,7 @@ 344F2498200FD03200CFB4F4 /* AttachmentApprovalViewController.swift */, 344F248220069E9B00CFB4F4 /* CountryCodeViewController.h */, 344F248320069E9B00CFB4F4 /* CountryCodeViewController.m */, + 34382269209B63500094FEB7 /* EditContactShareNameViewController.swift */, 346129DC1FD5C02900532771 /* LockInteractionController.h */, 346129DD1FD5C02900532771 /* LockInteractionController.m */, 344F2496200FD03200CFB4F4 /* MessageApprovalViewController.swift */, @@ -3098,6 +3101,7 @@ 451F8A481FD715BA005CB9DA /* OWSContactAvatarBuilder.m in Sources */, 34382266209A4E400094FEB7 /* ApproveContactShareViewController.swift in Sources */, 4503F1C3204711D300CEE724 /* OWS107LegacySounds.m in Sources */, + 3438226A209B63500094FEB7 /* EditContactShareNameViewController.swift in Sources */, 346129A61FD1F09100532771 /* OWSContactsManager.m in Sources */, 4598198F204E2F28009414F2 /* OWS108CallLoggingPreference.m in Sources */, 346129D21FD2085A00532771 /* CommonStrings.swift in Sources */, diff --git a/Signal/src/ViewControllers/ContactViewController.swift b/Signal/src/ViewControllers/ContactViewController.swift index ecb87d694..e0c1e6ee5 100644 --- a/Signal/src/ViewControllers/ContactViewController.swift +++ b/Signal/src/ViewControllers/ContactViewController.swift @@ -14,7 +14,7 @@ class TappableView: UIView { // MARK: - Initializers - @available(*, unavailable, message: "use init(call:) constructor instead.") + @available(*, unavailable, message: "use other constructor instead.") required init?(coder aDecoder: NSCoder) { fatalError("Unimplemented") } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 8de7b1a91..a9c36920c 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -445,6 +445,21 @@ /* Label for the 'street' field of a contact's address. */ "CONTACT_FIELD_ADDRESS_STREET" = "Street"; +/* Label for the 'family name' field of a contact. */ +"CONTACT_FIELD_FAMILY_NAME" = "Family Name"; + +/* Label for the 'given name' field of a contact. */ +"CONTACT_FIELD_GIVEN_NAME" = "Given Name"; + +/* Label for the 'middle name' field of a contact. */ +"CONTACT_FIELD_MIDDLE_NAME" = "Middle Name"; + +/* Label for the 'name prefix' field of a contact. */ +"CONTACT_FIELD_NAME_PREFIX" = "Prefix"; + +/* Label for the 'name suffix' field of a contact. */ +"CONTACT_FIELD_NAME_SUFFIX" = "Suffix"; + /* Label for a contact's phone number. */ "CONTACT_PHONE" = "Phone"; @@ -460,6 +475,9 @@ /* Title for the 'Approve contact share' view. */ "CONTACT_SHARE_APPROVAL_VIEW_TITLE" = "Share Contact"; +/* Title for the 'edit contact share name' view. */ +"CONTACT_SHARE_EDIT_NAME_VIEW_TITLE" = "Edit Name"; + /* Indicates that a contact has no name. */ "CONTACT_WITHOUT_NAME" = "Unnamed Contact"; diff --git a/SignalMessaging/attachments/ApproveContactShareViewController.swift b/SignalMessaging/attachments/ApproveContactShareViewController.swift index d3ea3d45e..d44f4faec 100644 --- a/SignalMessaging/attachments/ApproveContactShareViewController.swift +++ b/SignalMessaging/attachments/ApproveContactShareViewController.swift @@ -15,7 +15,7 @@ public protocol ApproveContactShareViewControllerDelegate: class { class ContactShareField: NSObject { - var isIncludedFlag = true + private var isIncludedFlag = true func localizedLabel() -> String { preconditionFailure("This method must be overridden") @@ -25,9 +25,13 @@ class ContactShareField: NSObject { return isIncludedFlag } - func setIsIncluded(isIncluded: Bool) { + func setIsIncluded(_ isIncluded: Bool) { isIncludedFlag = isIncluded } + + func applyToContact(contact: OWSContact) { + preconditionFailure("This method must be overridden") + } } // MARK: - @@ -45,6 +49,17 @@ class ContactSharePhoneNumber: ContactShareField { override func localizedLabel() -> String { return value.localizedLabel() } + + override func applyToContact(contact: OWSContact) { + assert(isIncluded()) + + var values = [OWSContactPhoneNumber]() + if let oldValues = contact.phoneNumbers { + values += oldValues + } + values.append(value) + contact.phoneNumbers = values + } } // MARK: - @@ -62,6 +77,17 @@ class ContactShareEmail: ContactShareField { override func localizedLabel() -> String { return value.localizedLabel() } + + override func applyToContact(contact: OWSContact) { + assert(isIncluded()) + + var values = [OWSContactEmail]() + if let oldValues = contact.emails { + values += oldValues + } + values.append(value) + contact.emails = values + } } // MARK: - @@ -79,6 +105,17 @@ class ContactShareAddress: ContactShareField { override func localizedLabel() -> String { return value.localizedLabel() } + + override func applyToContact(contact: OWSContact) { + assert(isIncluded()) + + var values = [OWSContactAddress]() + if let oldValues = contact.addresses { + values += oldValues + } + values.append(value) + contact.addresses = values + } } // MARK: - @@ -158,23 +195,25 @@ class ContactShareFieldView: UIView { guard sender.state == .recognized else { return } - checkbox.isSelected = !checkbox.isSelected + field.setIsIncluded(!field.isIncluded()) + checkbox.isSelected = field.isIncluded() } } // MARK: - @objc -public class ApproveContactShareViewController: OWSViewController { +public class ApproveContactShareViewController: OWSViewController, EditContactShareNameViewControllerDelegate { weak var delegate: ApproveContactShareViewControllerDelegate? - let contactsManager: OWSContactsManager + let contactsManager: OWSContactsManager - let contactShare: OWSContact + var contactShare: OWSContact - var fields = [ContactShareField]() var fieldViews = [ContactShareFieldView]() + var nameLabel: UILabel! + // MARK: Initializers @available(*, unavailable, message:"use other constructor instead.") @@ -194,7 +233,6 @@ public class ApproveContactShareViewController: OWSViewController { } func buildFields() { - var fields = [ContactShareField]() var fieldViews = [ContactShareFieldView]() // TODO: Avatar @@ -202,7 +240,6 @@ public class ApproveContactShareViewController: OWSViewController { if let phoneNumbers = contactShare.phoneNumbers { for phoneNumber in phoneNumbers { let field = ContactSharePhoneNumber(phoneNumber) - fields.append(field) let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in guard let strongSelf = self else { return UIView() } return strongSelf.previewView(forPhoneNumber: phoneNumber) @@ -214,7 +251,6 @@ public class ApproveContactShareViewController: OWSViewController { if let emails = contactShare.emails { for email in emails { let field = ContactShareEmail(email) - fields.append(field) let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in guard let strongSelf = self else { return UIView() } return strongSelf.previewView(forEmail: email) @@ -226,7 +262,6 @@ public class ApproveContactShareViewController: OWSViewController { if let addresses = contactShare.addresses { for address in addresses { let field = ContactShareAddress(address) - fields.append(field) let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in guard let strongSelf = self else { return UIView() } return strongSelf.previewView(forAddress: address) @@ -235,7 +270,6 @@ public class ApproveContactShareViewController: OWSViewController { } } - self.fields = fields self.fieldViews = fieldViews } @@ -290,7 +324,7 @@ public class ApproveContactShareViewController: OWSViewController { if canShareContact() { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON", comment: "Label for 'send' button in the 'attachment approval' dialog."), - style: .plain, target: self, action: #selector(didPressSendButton)) + style: .plain, target: self, action: #selector(didPressSendButton)) } else { self.navigationItem.rightBarButtonItem = nil } @@ -391,6 +425,7 @@ public class ApproveContactShareViewController: OWSViewController { stackView.axis = .horizontal stackView.alignment = .center stackView.layoutMargins = .zero + stackView.spacing = 10 row.addSubview(stackView) stackView.autoPinLeadingToSuperviewMargin() stackView.autoPinTrailingToSuperviewMargin() @@ -398,6 +433,7 @@ public class ApproveContactShareViewController: OWSViewController { stackView.autoPinBottomToSuperviewMargin() let nameLabel = UILabel() + self.nameLabel = nameLabel nameLabel.text = contactShare.displayName nameLabel.font = UIFont.ows_dynamicTypeBody nameLabel.textColor = UIColor.ows_materialBlue @@ -518,6 +554,24 @@ public class ApproveContactShareViewController: OWSViewController { // MARK: - + func filteredContactShare() -> OWSContact { + let result = self.contactShare.newContact(withNamePrefix: self.contactShare.namePrefix, + givenName: self.contactShare.givenName, + middleName: self.contactShare.middleName, + familyName: self.contactShare.familyName, + nameSuffix: self.contactShare.nameSuffix) + + for fieldView in fieldViews { + if fieldView.field.isIncluded() { + fieldView.field.applyToContact(contact: result) + } + } + + return result + } + + // MARK: - + func didPressSendButton() { Logger.info("\(logTag) \(#function)") @@ -526,7 +580,10 @@ public class ApproveContactShareViewController: OWSViewController { return } - delegate.approveContactShare(self, didApproveContactShare: contactShare) + let filteredContactShare = self.filteredContactShare() + assert(filteredContactShare.ows_isValid()) + + delegate.approveContactShare(self, didApproveContactShare: filteredContactShare) } func didPressCancel() { @@ -543,6 +600,17 @@ public class ApproveContactShareViewController: OWSViewController { func didPressEditName() { Logger.info("\(logTag) \(#function)") - // TODO: + let view = EditContactShareNameViewController(contactShare: contactShare, delegate: self) + self.navigationController?.pushViewController(view, animated: true) + } + + // MARK: - EditContactShareNameViewControllerDelegate + + public func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: OWSContact) { + self.contactShare = contactShare + + nameLabel.text = contactShare.displayName + + self.updateNavigationBar() } } diff --git a/SignalMessaging/attachments/EditContactShareNameViewController.swift b/SignalMessaging/attachments/EditContactShareNameViewController.swift new file mode 100644 index 000000000..496efc4ff --- /dev/null +++ b/SignalMessaging/attachments/EditContactShareNameViewController.swift @@ -0,0 +1,347 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation +import SignalServiceKit + +@objc +public protocol ContactNameFieldViewDelegate: class { + func nameFieldDidChange() +} + +// MARK: - + +class ContactNameFieldView: UIView { + weak var delegate: ContactNameFieldViewDelegate? + + let name: String + let initialValue: String? + + var valueView: UITextField! + + var hasUnsavedChanges = false + + // MARK: - Initializers + + @available(*, unavailable, message: "use other constructor instead.") + required init?(coder aDecoder: NSCoder) { + fatalError("Unimplemented") + } + + required init(name: String, value: String?, delegate: ContactNameFieldViewDelegate) { + self.name = name + self.initialValue = value + self.delegate = delegate + + super.init(frame: CGRect.zero) + + self.isUserInteractionEnabled = true + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped))) + + createContents() + } + + func createContents() { + let vMargin = CGFloat(10) + self.layoutMargins = UIEdgeInsets(top: vMargin, left: 0, bottom: vMargin, right: 0) + + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.alignment = .center + stackView.layoutMargins = .zero + stackView.spacing = 10 + self.addSubview(stackView) + stackView.autoPinTopToSuperviewMargin() + stackView.autoPinBottomToSuperviewMargin() + stackView.autoPinLeadingToSuperviewMargin() + stackView.autoPinTrailingToSuperviewMargin() + + let nameLabel = UILabel() + nameLabel.text = name + nameLabel.font = UIFont.ows_dynamicTypeBody + nameLabel.textColor = UIColor.ows_materialBlue + nameLabel.lineBreakMode = .byTruncatingTail + stackView.addArrangedSubview(nameLabel) + nameLabel.setContentHuggingHigh() + nameLabel.setCompressionResistanceHigh() + + valueView = UITextField() + if let initialValue = initialValue { + valueView.text = initialValue + } + valueView.font = UIFont.ows_dynamicTypeBody + valueView.textColor = UIColor.black + stackView.addArrangedSubview(valueView) + + valueView.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) + } + + func wasTapped(sender: UIGestureRecognizer) { + Logger.info("\(self.logTag) \(#function)") + + guard sender.state == .recognized else { + return + } + + valueView.becomeFirstResponder() + } + + func textFieldDidChange(sender: UITextField) { + Logger.info("\(self.logTag) \(#function)") + + hasUnsavedChanges = true + + guard let delegate = self.delegate else { + owsFail("\(logTag) missing delegate.") + return + } + + delegate.nameFieldDidChange() + } + + public func value() -> String { + guard let value = valueView.text else { + return "" + } + return value + } +} + +// MARK: - + +@objc +public protocol EditContactShareNameViewControllerDelegate: class { + func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: OWSContact) +} + +// MARK: - + +@objc +public class EditContactShareNameViewController: OWSViewController, ContactNameFieldViewDelegate { + weak var delegate: EditContactShareNameViewControllerDelegate? + + let contactShare: OWSContact + + var namePrefixView: ContactNameFieldView! + var givenNameView: ContactNameFieldView! + var middleNameView: ContactNameFieldView! + var familyNameView: ContactNameFieldView! + var nameSuffixView: ContactNameFieldView! + + var fieldViews = [ContactNameFieldView]() + + // MARK: Initializers + + @available(*, unavailable, message:"use other constructor instead.") + required public init?(coder aDecoder: NSCoder) { + fatalError("unimplemented") + } + + @objc + required public init(contactShare: OWSContact, delegate: EditContactShareNameViewControllerDelegate) { + self.contactShare = contactShare + self.delegate = delegate + + super.init(nibName: nil, bundle: nil) + + buildFields() + } + + func buildFields() { + namePrefixView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_NAME_PREFIX", comment: "Label for the 'name prefix' field of a contact."), + value: contactShare.namePrefix, delegate: self) + givenNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_GIVEN_NAME", comment: "Label for the 'given name' field of a contact."), + value: contactShare.givenName, delegate: self) + middleNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_MIDDLE_NAME", comment: "Label for the 'middle name' field of a contact."), + value: contactShare.middleName, delegate: self) + familyNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_FAMILY_NAME", comment: "Label for the 'family name' field of a contact."), + value: contactShare.familyName, delegate: self) + nameSuffixView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_NAME_SUFFIX", comment: "Label for the 'name suffix' field of a contact."), + value: contactShare.nameSuffix, delegate: self) + fieldViews = [ + namePrefixView , + givenNameView , + middleNameView , + familyNameView , + nameSuffixView + ] + } + + override public var canBecomeFirstResponder: Bool { + return true + } + + // MARK: - View Lifecycle + + override public func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + updateNavigationBar() + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + } + + override public func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + } + + override public func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + } + + override public func loadView() { + super.loadView() + + self.navigationItem.title = NSLocalizedString("CONTACT_SHARE_EDIT_NAME_VIEW_TITLE", + comment: "Title for the 'edit contact share name' view.") + + self.view.preservesSuperviewLayoutMargins = false + self.view.backgroundColor = UIColor.white + + updateContent() + + updateNavigationBar() + } + + func hasUnsavedChanges() -> Bool { + for fieldView in fieldViews { + if fieldView.hasUnsavedChanges { + return true + } + } + return false + } + + func updateNavigationBar() { + self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, + target: self, + action: #selector(didPressCancel)) + + if hasUnsavedChanges() { + self.navigationItem.rightBarButtonItem = + UIBarButtonItem(barButtonSystemItem: .save, + target: self, + action: #selector(didPressSave)) + } else { + self.navigationItem.rightBarButtonItem = nil + } + } + + private func updateContent() { + SwiftAssertIsOnMainThread(#function) + + guard let rootView = self.view else { + owsFail("\(logTag) missing root view.") + return + } + + for subview in rootView.subviews { + subview.removeFromSuperview() + } + + let scrollView = UIScrollView() + scrollView.preservesSuperviewLayoutMargins = false + self.view.addSubview(scrollView) + scrollView.layoutMargins = .zero + scrollView.autoPinWidthToSuperview() + scrollView.autoPin(toTopLayoutGuideOf: self, withInset: 0) + scrollView.autoPinEdge(toSuperviewEdge: .bottom) + + let fieldsView = createFieldsView() + + // See notes on how to use UIScrollView with iOS Auto Layout: + // + // https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-6_0/ + scrollView.addSubview(fieldsView) + fieldsView.autoPinLeadingToSuperviewMargin() + fieldsView.autoPinTrailingToSuperviewMargin() + fieldsView.autoPinEdge(toSuperviewEdge: .top) + fieldsView.autoPinEdge(toSuperviewEdge: .bottom) + } + + private func createFieldsView() -> UIView { + SwiftAssertIsOnMainThread(#function) + + let fieldsView = UIView.container() + fieldsView.layoutMargins = .zero + fieldsView.preservesSuperviewLayoutMargins = false + + 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) + fieldsView.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() + } + fieldsView.addSubview(row) + row.autoPinLeadingToSuperviewMargin(withInset: self.hMargin) + row.autoPinTrailingToSuperviewMargin(withInset: self.hMargin) + if let lastRow = lastRow { + row.autoPinEdge(.top, to: .bottom, of: lastRow, withOffset: 0) + } else { + row.autoPinEdge(toSuperviewEdge: .top, withInset: 0) + } + lastRow = row + } + + for fieldView in fieldViews { + addRow(fieldView) + } + + lastRow?.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0) + + return fieldsView + } + + private let hMargin = CGFloat(16) + + // MARK: - + + func didPressSave() { + Logger.info("\(logTag) \(#function)") + + let modifiedContactShare = contactShare.copy(withNamePrefix: namePrefixView.value(), + givenName: givenNameView.value(), + middleName: middleNameView.value(), + familyName: familyNameView.value(), + nameSuffix: nameSuffixView.value()) + + guard let delegate = self.delegate else { + owsFail("\(logTag) missing delegate.") + return + } + + delegate.editContactShareNameView(self, didEditContactShare: modifiedContactShare) + + self.navigationController?.popViewController(animated: true) + } + + func didPressCancel() { + Logger.info("\(logTag) \(#function)") + + self.navigationController?.popViewController(animated: true) + } + + // MARK: - ContactNameFieldViewDelegate + + public func nameFieldDidChange() { + updateNavigationBar() + } +} diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.h b/SignalServiceKit/src/Messages/Interactions/OWSContact.h index d419bce32..ecd8fc5d0 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.h @@ -128,6 +128,20 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value); - (NSString *)debugDescription; +#pragma mark - Creation and Derivation + +- (OWSContact *)newContactWithNamePrefix:(nullable NSString *)namePrefix + givenName:(nullable NSString *)givenName + middleName:(nullable NSString *)middleName + familyName:(nullable NSString *)familyName + nameSuffix:(nullable NSString *)nameSuffix; + +- (OWSContact *)copyContactWithNamePrefix:(nullable NSString *)namePrefix + givenName:(nullable NSString *)givenName + middleName:(nullable NSString *)middleName + familyName:(nullable NSString *)familyName + nameSuffix:(nullable NSString *)nameSuffix; + @end #pragma mark - @@ -154,10 +168,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value); + (nullable OWSSignalServiceProtosDataMessageContact *)protoForContact:(OWSContact *)contact; + (OWSContact *_Nullable)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage; -//#pragma mark - Old Contacts -// -//+ (nullable OWSContact *)contactForOldContact:(Contact *)oldContact - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.m b/SignalServiceKit/src/Messages/Interactions/OWSContact.m index a0c4398e3..5e1130e55 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.m @@ -124,7 +124,7 @@ NSString *NSStringForContactEmailType(OWSContactEmailType value) DDLogWarn(@"%@ invalid email: %@.", self.logTag, self.email); return NO; } - return YES; + return YES; } - (NSString *)localizedLabel @@ -203,7 +203,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) DDLogWarn(@"%@ invalid address; empty.", self.logTag); return NO; } - return YES; + return YES; } - (NSString *)localizedLabel @@ -362,6 +362,13 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) } } +- (void)updateDisplayName +{ + _displayName = nil; + + [self ensureDisplayName]; +} + - (NSString *)debugDescription { NSMutableString *result = [NSMutableString new]; @@ -400,6 +407,55 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) return result; } +- (OWSContact *)newContactWithNamePrefix:(nullable NSString *)namePrefix + givenName:(nullable NSString *)givenName + middleName:(nullable NSString *)middleName + familyName:(nullable NSString *)familyName + nameSuffix:(nullable NSString *)nameSuffix +{ + OWSContact *newContact = [OWSContact new]; + + [newContact setNamePrefix:namePrefix + givenName:givenName + middleName:middleName + familyName:familyName + nameSuffix:nameSuffix]; + + return newContact; +} + +- (OWSContact *)copyContactWithNamePrefix:(nullable NSString *)namePrefix + givenName:(nullable NSString *)givenName + middleName:(nullable NSString *)middleName + familyName:(nullable NSString *)familyName + nameSuffix:(nullable NSString *)nameSuffix +{ + OWSContact *contactCopy = [self copy]; + + [contactCopy setNamePrefix:namePrefix + givenName:givenName + middleName:middleName + familyName:familyName + nameSuffix:nameSuffix]; + + return contactCopy; +} + +- (void)setNamePrefix:(nullable NSString *)namePrefix + givenName:(nullable NSString *)givenName + middleName:(nullable NSString *)middleName + familyName:(nullable NSString *)familyName + nameSuffix:(nullable NSString *)nameSuffix +{ + self.namePrefix = namePrefix.ows_stripped; + self.givenName = givenName.ows_stripped; + self.middleName = middleName.ows_stripped; + self.familyName = familyName.ows_stripped; + self.nameSuffix = nameSuffix.ows_stripped; + + [self updateDisplayName]; +} + @end #pragma mark - @@ -960,76 +1016,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) return result; } -//#pragma mark - Old Contacts -// -//+ (nullable OWSContact *)contactForOldContact:(Contact *)oldContact -//{ -// OWSAssert(oldContact); -// OWSAssert(oldContact.cnContact); -// -//// DDLogDebug(@"%@ in %s with contact: %@", self.logTag, __PRETTY_FUNCTION__, contact); -// -// return [self contactForSystemContact:oldContact.cnContact]; -// -//// OWSContact *newContact = [OWSContact new]; -//// -//// newContact.givenName = oldContact.firstName; -//// newContact.familyName = oldContact.lastName; -//// -//// NSMutableArray< -//// OWSContactPhoneNumber *> *newPhoneNumbers = [NSMutableArray new]; -//// for (NSString *oldPhoneNumber in oldContact.userTextPhoneNumbers) { -//// OWSContactPhoneNumber *newPhoneNumber = [OWSContactPhoneNumber new]; -//// newPhoneNumber.phoneNumber = oldPhoneNumber; -//// newPhoneNumber.phoneType = OWSContactPhoneType_Custom; -//// [newPhoneNumbers addObject:newPhoneNumber]; -//// } -//// newContact.phoneNumbers = newPhoneNumbers; -//// -//// NSMutableArray< -//// OWSContactEmail *> *newEmails = [NSMutableArray new]; -//// for (NSString *oldEmail in oldContact.emails) { -//// OWSContactEmail *newEmail = [OWSContactEmail new]; -//// newEmail.email = oldEmail; -//// newEmail.emailType = OWSContactEmailType_Custom; -//// [newEmails addObject:newEmail]; -//// } -//// newContact.emails = newEmails; -//// -//// @interface Contact : MTLModel -//// -//// @property (nullable, readonly, nonatomic) NSString *firstName; -//// @property (nullable, readonly, nonatomic) NSString *lastName; -//// @property (readonly, nonatomic) NSString *fullName; -//// @property (readonly, nonatomic) NSString *comparableNameFirstLast; -//// @property (readonly, nonatomic) NSString *comparableNameLastFirst; -//// @property (readonly, nonatomic) NSArray *parsedPhoneNumbers; -//// @property (readonly, nonatomic) NSArray *userTextPhoneNumbers; -//// @property (readonly, nonatomic) NSArray *emails; -//// @property (readonly, nonatomic) NSString *uniqueId; -//// @property (nonatomic, readonly) BOOL isSignalContact; -////#if TARGET_OS_IOS -//// @property (nullable, readonly, nonatomic) UIImage *image; -//// @property (nullable, nonatomic, readonly) CNContact *cnContact; -////#endif // TARGET_OS_IOS -//// -//// - (NSArray *)signalRecipientsWithTransaction:(YapDatabaseReadTransaction *)transaction; -//// // TODO: Remove this method. -//// - (NSArray *)textSecureIdentifiers; -//// -////#if TARGET_OS_IOS -//// -//// - (instancetype)initWithSystemContact:(CNContact *)contact NS_AVAILABLE_IOS(9_0); -//// -//// - (NSString *)nameForPhoneNumber:(NSString *)recipientId; -//// -////#endif // TARGET_OS_IOS -//// -//// + (NSComparator)comparatorSortingNamesByFirstThenLast:(BOOL)firstNameOrdering; -//// -//// @end -//} - @end NS_ASSUME_NONNULL_END