Merge branch 'charlesmchen/editContactShareView'
This commit is contained in:
commit
4bf08280dc
@ -48,6 +48,8 @@
|
||||
34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; };
|
||||
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 */; };
|
||||
@ -161,6 +163,7 @@
|
||||
3478506C1FD9B78A007B8332 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347850681FD9B78A007B8332 /* NoopNotificationsManager.swift */; };
|
||||
347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3478506F1FDAEB16007B8332 /* OWSUserProfile.m */; };
|
||||
347850721FDAEB17007B8332 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 347850701FDAEB16007B8332 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
348BB254209CD4B80047AEC2 /* ContactFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348BB253209CD4B80047AEC2 /* ContactFieldView.swift */; };
|
||||
3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496744C2076768700080B5F /* OWSMessageBubbleView.m */; };
|
||||
3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; };
|
||||
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */; };
|
||||
@ -630,6 +633,8 @@
|
||||
34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ElegantIcons.ttf; sourceTree = "<group>"; };
|
||||
34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = "<group>"; };
|
||||
34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
34386A4E207D0C01009F5D9C /* HomeViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewCell.h; sourceTree = "<group>"; };
|
||||
34386A4F207D0C01009F5D9C /* HomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeViewController.h; sourceTree = "<group>"; };
|
||||
@ -755,6 +760,7 @@
|
||||
347850681FD9B78A007B8332 /* NoopNotificationsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoopNotificationsManager.swift; sourceTree = "<group>"; };
|
||||
3478506F1FDAEB16007B8332 /* OWSUserProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUserProfile.m; sourceTree = "<group>"; };
|
||||
347850701FDAEB16007B8332 /* OWSUserProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUserProfile.h; sourceTree = "<group>"; };
|
||||
348BB253209CD4B80047AEC2 /* ContactFieldView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactFieldView.swift; path = SignalMessaging/attachments/ContactFieldView.swift; sourceTree = SOURCE_ROOT; };
|
||||
348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceSleepManager.swift; sourceTree = "<group>"; };
|
||||
3495BC911F1426B800B478F5 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = translations/ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3496744B2076768600080B5F /* OWSMessageBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageBubbleView.h; sourceTree = "<group>"; };
|
||||
@ -1551,12 +1557,12 @@
|
||||
346129A11FD1F09100532771 /* contacts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3400C7941EAF99F4008A8584 /* SelectThreadViewController.h */,
|
||||
3400C7951EAF99F4008A8584 /* SelectThreadViewController.m */,
|
||||
346129A21FD1F09100532771 /* OWSContactsManager.h */,
|
||||
346129A31FD1F09100532771 /* OWSContactsManager.m */,
|
||||
34612A041FD7238500532771 /* OWSContactsSyncing.h */,
|
||||
34612A051FD7238500532771 /* OWSContactsSyncing.m */,
|
||||
3400C7941EAF99F4008A8584 /* SelectThreadViewController.h */,
|
||||
3400C7951EAF99F4008A8584 /* SelectThreadViewController.m */,
|
||||
346129AE1FD1F5D900532771 /* SystemContactsFetcher.swift */,
|
||||
);
|
||||
path = contacts;
|
||||
@ -1596,9 +1602,12 @@
|
||||
346129DB1FD5C02900532771 /* viewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
34382265209A4E3F0094FEB7 /* ApproveContactShareViewController.swift */,
|
||||
344F2498200FD03200CFB4F4 /* AttachmentApprovalViewController.swift */,
|
||||
348BB253209CD4B80047AEC2 /* ContactFieldView.swift */,
|
||||
344F248220069E9B00CFB4F4 /* CountryCodeViewController.h */,
|
||||
344F248320069E9B00CFB4F4 /* CountryCodeViewController.m */,
|
||||
34382269209B63500094FEB7 /* EditContactShareNameViewController.swift */,
|
||||
346129DC1FD5C02900532771 /* LockInteractionController.h */,
|
||||
346129DD1FD5C02900532771 /* LockInteractionController.m */,
|
||||
344F2496200FD03200CFB4F4 /* MessageApprovalViewController.swift */,
|
||||
@ -3051,6 +3060,7 @@
|
||||
346129E61FD5C0C600532771 /* OWSDatabaseMigrationRunner.m in Sources */,
|
||||
346129AB1FD1F0EE00532771 /* OWSFormat.m in Sources */,
|
||||
344F249C200FD03300CFB4F4 /* AttachmentApprovalViewController.swift in Sources */,
|
||||
348BB254209CD4B80047AEC2 /* ContactFieldView.swift in Sources */,
|
||||
451F8A461FD715BA005CB9DA /* OWSGroupAvatarBuilder.m in Sources */,
|
||||
347850591FD9972E007B8332 /* SwiftSingletons.swift in Sources */,
|
||||
344F248720069ECB00CFB4F4 /* ModalActivityIndicatorViewController.swift in Sources */,
|
||||
@ -3093,7 +3103,9 @@
|
||||
451F8A3F1FD71455005CB9DA /* OWSTableViewController.m in Sources */,
|
||||
451F8A351FD710DE005CB9DA /* Searcher.swift in Sources */,
|
||||
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 */,
|
||||
|
||||
@ -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")
|
||||
}
|
||||
@ -367,48 +367,14 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
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)
|
||||
lastRow = row
|
||||
}
|
||||
|
||||
let addRow: ((UIView) -> Void) = { (row) in
|
||||
if lastRow != nil {
|
||||
addSpacerRow()
|
||||
}
|
||||
fieldsView.addSubview(row)
|
||||
row.autoPinLeadingToSuperviewMargin()
|
||||
row.autoPinTrailingToSuperviewMargin()
|
||||
if let lastRow = lastRow {
|
||||
row.autoPinEdge(.top, to: .bottom, of: lastRow)
|
||||
} else {
|
||||
row.autoPinEdge(toSuperviewEdge: .top)
|
||||
}
|
||||
lastRow = row
|
||||
}
|
||||
var rows = [UIView]()
|
||||
|
||||
if viewMode == .nonSystemContact {
|
||||
addRow(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_NEW_CONTACT",
|
||||
rows.append(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_NEW_CONTACT",
|
||||
comment: "Label for 'new contact' button in conversation settings view."),
|
||||
action: #selector(didPressCreateNewContact)))
|
||||
|
||||
addRow(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT",
|
||||
rows.append(createActionRow(labelText: NSLocalizedString("CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT",
|
||||
comment: "Label for 'new contact' button in conversation settings view."),
|
||||
action: #selector(didPressAddToExistingContact)))
|
||||
}
|
||||
@ -424,7 +390,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
for phoneNumber in contact.phoneNumbers {
|
||||
let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber)
|
||||
|
||||
addRow(createNameValueRow(name: phoneNumber.localizedLabel(),
|
||||
rows.append(createNameValueRow(name: phoneNumber.localizedLabel(),
|
||||
value: formattedPhoneNumber,
|
||||
actionBlock: {
|
||||
guard let url = NSURL(string: "tel:\(phoneNumber.phoneNumber)") else {
|
||||
@ -436,7 +402,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
}
|
||||
|
||||
for email in contact.emails {
|
||||
addRow(createNameValueRow(name: email.localizedLabel(),
|
||||
rows.append(createNameValueRow(name: email.localizedLabel(),
|
||||
value: email.email,
|
||||
actionBlock: {
|
||||
guard let url = NSURL(string: "mailto:\(email.email)") else {
|
||||
@ -449,9 +415,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
// TODO: Should we present addresses here too? How?
|
||||
|
||||
lastRow?.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
|
||||
return fieldsView
|
||||
return ContactFieldView(rows: rows, hMargin: hMargin)
|
||||
}
|
||||
|
||||
private let hMargin = CGFloat(16)
|
||||
@ -690,10 +654,10 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
contactViewController.allowsActions = false
|
||||
contactViewController.allowsEditing = true
|
||||
contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton, style: .plain, target: self, action: #selector(didFinishEditingContact))
|
||||
contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton,
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(didFinishEditingContact))
|
||||
contactViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: CommonStrings.cancelButton,
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(didFinishEditingContact))
|
||||
|
||||
self.navigationController?.pushViewController(contactViewController, animated: true)
|
||||
|
||||
|
||||
@ -60,7 +60,8 @@ public class ContactsPicker: OWSViewController, UITableViewDelegate, UITableView
|
||||
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
|
||||
CNContactThumbnailImageDataKey as CNKeyDescriptor,
|
||||
CNContactPhoneNumbersKey as CNKeyDescriptor,
|
||||
CNContactEmailAddressesKey as CNKeyDescriptor
|
||||
CNContactEmailAddressesKey as CNKeyDescriptor,
|
||||
CNContactPostalAddressesKey as CNKeyDescriptor
|
||||
]
|
||||
|
||||
// MARK: - Lifecycle Methods
|
||||
|
||||
@ -117,6 +117,7 @@ typedef enum : NSUInteger {
|
||||
#pragma mark -
|
||||
|
||||
@interface ConversationViewController () <AttachmentApprovalViewControllerDelegate,
|
||||
ApproveContactShareViewControllerDelegate,
|
||||
AVAudioPlayerDelegate,
|
||||
CNContactViewControllerDelegate,
|
||||
ContactEditingDelegate,
|
||||
@ -2522,6 +2523,7 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
|
||||
#pragma mark - Attachment Picking: Contacts
|
||||
|
||||
- (void)chooseContactForSending
|
||||
{
|
||||
ContactsPicker *contactsPicker =
|
||||
@ -2946,6 +2948,26 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendContactShare:(OWSContact *)contactShare
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert(contactShare);
|
||||
|
||||
DDLogVerbose(@"%@ Sending contact share.", self.logTag);
|
||||
|
||||
BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread];
|
||||
TSOutgoingMessage *message = [ThreadUtil sendMessageWithContactShare:contactShare
|
||||
inThread:self.thread
|
||||
messageSender:self.messageSender
|
||||
completion:nil];
|
||||
|
||||
[self messageWasSent:message];
|
||||
|
||||
if (didAddToProfileWhitelist) {
|
||||
[self ensureDynamicInteractions];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURL *)videoTempFolder
|
||||
{
|
||||
NSString *temporaryDirectory = NSTemporaryDirectory();
|
||||
@ -4915,10 +4937,27 @@ interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransiti
|
||||
|
||||
- (void)contactsPicker:(ContactsPicker *)contactsPicker didSelectContact:(Contact *)contact
|
||||
{
|
||||
OWSAssert(contact);
|
||||
OWSAssert(contact.cnContact);
|
||||
|
||||
DDLogDebug(@"%@ in %s with contact: %@", self.logTag, __PRETTY_FUNCTION__, contact);
|
||||
|
||||
// TODO actually build contact message.
|
||||
self.inputToolbar.messageText = contact.fullName;
|
||||
OWSContact *_Nullable contactShare = [OWSContacts contactForSystemContact:contact.cnContact];
|
||||
if (!contactShare) {
|
||||
DDLogError(@"%@ Could not convert system contact.", self.logTag);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: We should probably show this in the same navigation view controller.
|
||||
ApproveContactShareViewController *approveContactShare =
|
||||
[[ApproveContactShareViewController alloc] initWithContactShare:contactShare
|
||||
contactsManager:self.contactsManager
|
||||
delegate:self];
|
||||
|
||||
UINavigationController *navigationController =
|
||||
[[UINavigationController alloc] initWithRootViewController:approveContactShare];
|
||||
[self dismissKeyBoard];
|
||||
[self presentViewController:navigationController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)contactsPicker:(ContactsPicker *)contactsPicker didSelectMultipleContacts:(NSArray<Contact *> *)contacts
|
||||
@ -4932,6 +4971,27 @@ interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransiti
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - ApproveContactShareViewControllerDelegate
|
||||
|
||||
- (void)approveContactShare:(ApproveContactShareViewController *)approveContactShare
|
||||
didApproveContactShare:(OWSContact *)contactShare
|
||||
{
|
||||
DDLogInfo(@"%@ in %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
[self dismissViewControllerAnimated:YES
|
||||
completion:^{
|
||||
[self sendContactShare:contactShare];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)approveContactShare:(ApproveContactShareViewController *)approveContactShare
|
||||
didCancelContactShare:(OWSContact *)contactShare
|
||||
{
|
||||
DDLogInfo(@"%@ in %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -120,9 +120,6 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
||||
scrollView.autoPinWidthToSuperview(withMargin: 0)
|
||||
scrollView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
|
||||
|
||||
// See notes on how to use UIScrollView with iOS Auto Layout:
|
||||
//
|
||||
// https://developer.apple.com/library/content/releasenotes/General/RN-iOSSDK-6_0/
|
||||
let contentView = UIView.container()
|
||||
self.contentView = contentView
|
||||
scrollView.addSubview(contentView)
|
||||
|
||||
@ -418,9 +418,48 @@
|
||||
/* No comment provided by engineer. */
|
||||
"CONTACT_DETAIL_COMM_TYPE_INSECURE" = "Unregistered Number";
|
||||
|
||||
/* Label for the 'edit name' button in the contact share approval view. */
|
||||
"CONTACT_EDIT_NAME_BUTTON" = "Edit";
|
||||
|
||||
/* Label for a contact's email address. */
|
||||
"CONTACT_EMAIL" = "Email";
|
||||
|
||||
/* Label for the 'city' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_CITY" = "City";
|
||||
|
||||
/* Label for the 'country' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_COUNTRY" = "Country";
|
||||
|
||||
/* Label for the 'neighborhood' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_NEIGHBORHOOD" = "Neighborhood";
|
||||
|
||||
/* Label for the 'pobox' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_POBOX" = "P.O. Box";
|
||||
|
||||
/* Label for the 'postcode' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_POSTCODE" = "Post Code";
|
||||
|
||||
/* Label for the 'region' field of a contact's address. */
|
||||
"CONTACT_FIELD_ADDRESS_REGION" = "Region";
|
||||
|
||||
/* 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";
|
||||
|
||||
@ -433,6 +472,12 @@
|
||||
/* navbar title for contact picker when sharing a contact */
|
||||
"CONTACT_PICKER_TITLE" = "Select Contact";
|
||||
|
||||
/* 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";
|
||||
|
||||
|
||||
@ -0,0 +1,533 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalServiceKit
|
||||
|
||||
@objc
|
||||
public protocol ApproveContactShareViewControllerDelegate: class {
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didApproveContactShare contactShare: OWSContact)
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didCancelContactShare contactShare: OWSContact)
|
||||
}
|
||||
|
||||
protocol ContactShareField: class {
|
||||
|
||||
func localizedLabel() -> String
|
||||
|
||||
func isIncluded() -> Bool
|
||||
|
||||
func setIsIncluded(_ isIncluded: Bool)
|
||||
|
||||
func applyToContact(contact: OWSContact)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class ContactShareFieldBase<ContactFieldType: OWSContactField>: NSObject, ContactShareField {
|
||||
|
||||
let value: ContactFieldType
|
||||
|
||||
private var isIncludedFlag = true
|
||||
|
||||
required init(_ value: ContactFieldType) {
|
||||
self.value = value
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
func localizedLabel() -> String {
|
||||
return value.localizedLabel()
|
||||
}
|
||||
|
||||
func isIncluded() -> Bool {
|
||||
return isIncludedFlag
|
||||
}
|
||||
|
||||
func setIsIncluded(_ isIncluded: Bool) {
|
||||
isIncludedFlag = isIncluded
|
||||
}
|
||||
|
||||
func applyToContact(contact: OWSContact) {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class ContactSharePhoneNumber: ContactShareFieldBase<OWSContactPhoneNumber> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactPhoneNumber]()
|
||||
values += contact.phoneNumbers
|
||||
values.append(value)
|
||||
contact.phoneNumbers = values
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class ContactShareEmail: ContactShareFieldBase<OWSContactEmail> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactEmail]()
|
||||
values += contact.emails
|
||||
values.append(value)
|
||||
contact.emails = values
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class ContactShareAddress: ContactShareFieldBase<OWSContactAddress> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactAddress]()
|
||||
values += contact.addresses
|
||||
values.append(value)
|
||||
contact.addresses = values
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class ContactShareFieldView: UIView {
|
||||
|
||||
let field: ContactShareField
|
||||
|
||||
let previewViewBlock : (() -> UIView)
|
||||
|
||||
private var checkbox: UIButton!
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
@available(*, unavailable, message: "use init(call:) constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("Unimplemented")
|
||||
}
|
||||
|
||||
required init(field: ContactShareField, previewViewBlock : @escaping (() -> UIView)) {
|
||||
self.field = field
|
||||
self.previewViewBlock = previewViewBlock
|
||||
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
self.isUserInteractionEnabled = true
|
||||
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(wasTapped)))
|
||||
|
||||
createContents()
|
||||
}
|
||||
|
||||
let hSpacing = CGFloat(10)
|
||||
let hMargin = CGFloat(0)
|
||||
|
||||
func createContents() {
|
||||
self.layoutMargins.left = 0
|
||||
self.layoutMargins.right = 0
|
||||
|
||||
let checkbox = UIButton(type: .custom)
|
||||
self.checkbox = checkbox
|
||||
// TODO: Use real assets.
|
||||
checkbox.setTitle("☐", for: .normal)
|
||||
checkbox.setTitle("☒", for: .selected)
|
||||
checkbox.setTitleColor(UIColor.black, for: .normal)
|
||||
checkbox.setTitleColor(UIColor.black, for: .selected)
|
||||
checkbox.titleLabel?.font = UIFont.ows_dynamicTypeBody
|
||||
checkbox.isSelected = field.isIncluded()
|
||||
// Disable the checkbox; the entire row is hot.
|
||||
checkbox.isUserInteractionEnabled = false
|
||||
addSubview(checkbox)
|
||||
checkbox.autoPinEdge(toSuperviewEdge: .leading, withInset: hMargin)
|
||||
checkbox.autoVCenterInSuperview()
|
||||
checkbox.setCompressionResistanceHigh()
|
||||
checkbox.setContentHuggingHigh()
|
||||
|
||||
let nameLabel = UILabel()
|
||||
nameLabel.text = field.localizedLabel()
|
||||
nameLabel.font = UIFont.ows_dynamicTypeCaption1
|
||||
nameLabel.textColor = UIColor.black
|
||||
nameLabel.lineBreakMode = .byTruncatingTail
|
||||
addSubview(nameLabel)
|
||||
nameLabel.autoPinTopToSuperviewMargin()
|
||||
nameLabel.autoPinLeading(toTrailingEdgeOf: checkbox, offset: hSpacing)
|
||||
nameLabel.autoPinTrailingToSuperviewMargin(withInset: hMargin)
|
||||
|
||||
let previewView = previewViewBlock()
|
||||
addSubview(previewView)
|
||||
previewView.autoPinEdge(.top, to: .bottom, of: nameLabel, withOffset: 3)
|
||||
previewView.autoPinBottomToSuperviewMargin()
|
||||
previewView.autoPinLeading(toTrailingEdgeOf: checkbox, offset: hSpacing)
|
||||
previewView.autoPinTrailingToSuperviewMargin(withInset: hMargin)
|
||||
}
|
||||
|
||||
func wasTapped(sender: UIGestureRecognizer) {
|
||||
Logger.info("\(self.logTag) \(#function)")
|
||||
|
||||
guard sender.state == .recognized else {
|
||||
return
|
||||
}
|
||||
field.setIsIncluded(!field.isIncluded())
|
||||
checkbox.isSelected = field.isIncluded()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class ApproveContactShareViewController: OWSViewController, EditContactShareNameViewControllerDelegate {
|
||||
weak var delegate: ApproveContactShareViewControllerDelegate?
|
||||
|
||||
let contactsManager: OWSContactsManager
|
||||
|
||||
var contactShare: OWSContact
|
||||
|
||||
var fieldViews = [ContactShareFieldView]()
|
||||
|
||||
var nameLabel: UILabel!
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use other constructor instead.")
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("unimplemented")
|
||||
}
|
||||
|
||||
@objc
|
||||
required public init(contactShare: OWSContact, contactsManager: OWSContactsManager, delegate: ApproveContactShareViewControllerDelegate) {
|
||||
self.contactsManager = contactsManager
|
||||
self.contactShare = contactShare
|
||||
self.delegate = delegate
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
buildFields()
|
||||
}
|
||||
|
||||
func buildFields() {
|
||||
var fieldViews = [ContactShareFieldView]()
|
||||
|
||||
// TODO: Avatar
|
||||
|
||||
for phoneNumber in contactShare.phoneNumbers {
|
||||
let field = ContactSharePhoneNumber(phoneNumber)
|
||||
let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in
|
||||
guard let strongSelf = self else { return UIView() }
|
||||
return strongSelf.previewView(forPhoneNumber: phoneNumber)
|
||||
})
|
||||
fieldViews.append(fieldView)
|
||||
}
|
||||
for email in contactShare.emails {
|
||||
let field = ContactShareEmail(email)
|
||||
let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in
|
||||
guard let strongSelf = self else { return UIView() }
|
||||
return strongSelf.previewView(forEmail: email)
|
||||
})
|
||||
fieldViews.append(fieldView)
|
||||
}
|
||||
for address in contactShare.addresses {
|
||||
let field = ContactShareAddress(address)
|
||||
let fieldView = ContactShareFieldView(field: field, previewViewBlock: { [weak self] _ in
|
||||
guard let strongSelf = self else { return UIView() }
|
||||
return strongSelf.previewView(forAddress: address)
|
||||
})
|
||||
fieldViews.append(fieldView)
|
||||
}
|
||||
|
||||
self.fieldViews = fieldViews
|
||||
}
|
||||
|
||||
// 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_APPROVAL_VIEW_TITLE",
|
||||
comment: "Title for the 'Approve contact share' view.")
|
||||
|
||||
self.view.backgroundColor = UIColor.white
|
||||
|
||||
updateContent()
|
||||
|
||||
updateNavigationBar()
|
||||
}
|
||||
|
||||
// TODO: Surface error with resolution to user if not.
|
||||
func canShareContact() -> Bool {
|
||||
return contactShare.ows_isValid()
|
||||
}
|
||||
|
||||
func updateNavigationBar() {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(didPressCancel))
|
||||
|
||||
if canShareContact() {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON",
|
||||
comment: "Label for 'send' button in the 'attachment approval' dialog."),
|
||||
style: .plain, target: self, action: #selector(didPressSendButton))
|
||||
} 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()
|
||||
|
||||
scrollView.addSubview(fieldsView)
|
||||
// Use layoutMarginsGuide for views inside UIScrollView
|
||||
// that should have same width as scroll view.
|
||||
fieldsView.autoPinLeadingToSuperviewMargin()
|
||||
fieldsView.autoPinTrailingToSuperviewMargin()
|
||||
fieldsView.autoPinEdge(toSuperviewEdge: .top)
|
||||
fieldsView.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
fieldsView.setContentHuggingHorizontalLow()
|
||||
}
|
||||
|
||||
private func createFieldsView() -> UIView {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
var rows = [UIView]()
|
||||
|
||||
rows.append(createNameRow())
|
||||
|
||||
for fieldView in fieldViews {
|
||||
rows.append(fieldView)
|
||||
}
|
||||
|
||||
return ContactFieldView(rows: rows, hMargin: hMargin)
|
||||
}
|
||||
|
||||
private let hMargin = CGFloat(16)
|
||||
|
||||
func createNameRow() -> UIView {
|
||||
let nameVMargin = CGFloat(16)
|
||||
|
||||
let row = UIView()
|
||||
row.layoutMargins = UIEdgeInsets(top: nameVMargin, left: 0, bottom: nameVMargin, right: 0)
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .horizontal
|
||||
stackView.alignment = .center
|
||||
stackView.layoutMargins = .zero
|
||||
stackView.spacing = 10
|
||||
row.addSubview(stackView)
|
||||
stackView.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
let nameLabel = UILabel()
|
||||
self.nameLabel = nameLabel
|
||||
nameLabel.text = contactShare.displayName
|
||||
nameLabel.font = UIFont.ows_dynamicTypeBody
|
||||
nameLabel.textColor = UIColor.ows_materialBlue
|
||||
nameLabel.lineBreakMode = .byTruncatingTail
|
||||
stackView.addArrangedSubview(nameLabel)
|
||||
|
||||
let editNameLabel = UILabel()
|
||||
editNameLabel.text = NSLocalizedString("CONTACT_EDIT_NAME_BUTTON", comment: "Label for the 'edit name' button in the contact share approval view.")
|
||||
editNameLabel.font = UIFont.ows_dynamicTypeCaption1
|
||||
editNameLabel.textColor = UIColor.black
|
||||
stackView.addArrangedSubview(editNameLabel)
|
||||
editNameLabel.setContentHuggingHigh()
|
||||
editNameLabel.setCompressionResistanceHigh()
|
||||
|
||||
// Icon
|
||||
let iconName = (self.view.isRTL() ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator")
|
||||
guard let iconImage = UIImage(named: iconName) else {
|
||||
owsFail("\(logTag) missing icon.")
|
||||
return row
|
||||
}
|
||||
let iconView = UIImageView(image: iconImage.withRenderingMode(.alwaysTemplate))
|
||||
iconView.contentMode = .scaleAspectFit
|
||||
iconView.tintColor = UIColor.black.withAlphaComponent(0.6)
|
||||
stackView.addArrangedSubview(iconView)
|
||||
iconView.setContentHuggingHigh()
|
||||
iconView.setCompressionResistanceHigh()
|
||||
|
||||
row.isUserInteractionEnabled = true
|
||||
row.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didPressEditName)))
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func previewView(forPhoneNumber phoneNumber: OWSContactPhoneNumber) -> UIView {
|
||||
let label = UILabel()
|
||||
label.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber)
|
||||
label.font = UIFont.ows_dynamicTypeCaption1
|
||||
label.textColor = UIColor.ows_materialBlue
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
return label
|
||||
}
|
||||
|
||||
func previewView(forEmail email: OWSContactEmail) -> UIView {
|
||||
let label = UILabel()
|
||||
label.text = email.email
|
||||
label.font = UIFont.ows_dynamicTypeCaption1
|
||||
label.textColor = UIColor.ows_materialBlue
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
return label
|
||||
}
|
||||
|
||||
func previewView(forAddress address: OWSContactAddress) -> UIView {
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .leading
|
||||
stackView.spacing = 0
|
||||
stackView.layoutMargins = .zero
|
||||
|
||||
let tryToAddNameValue: ((String, String?) -> Void) = { (name, value) in
|
||||
guard let value = value else {
|
||||
return
|
||||
}
|
||||
guard value.count > 0 else {
|
||||
return
|
||||
}
|
||||
let row = UIView.container()
|
||||
|
||||
let nameLabel = UILabel()
|
||||
nameLabel.text = name
|
||||
nameLabel.font = UIFont.ows_dynamicTypeCaption1
|
||||
nameLabel.textColor = UIColor.black
|
||||
nameLabel.lineBreakMode = .byTruncatingTail
|
||||
row.addSubview(nameLabel)
|
||||
nameLabel.autoPinLeadingToSuperviewMargin()
|
||||
nameLabel.autoPinHeightToSuperview()
|
||||
nameLabel.setContentHuggingHigh()
|
||||
nameLabel.setCompressionResistanceHigh()
|
||||
|
||||
let valueLabel = UILabel()
|
||||
valueLabel.text = value
|
||||
valueLabel.font = UIFont.ows_dynamicTypeCaption1
|
||||
valueLabel.textColor = UIColor.ows_materialBlue
|
||||
valueLabel.lineBreakMode = .byTruncatingTail
|
||||
row.addSubview(valueLabel)
|
||||
valueLabel.autoPinLeading(toTrailingEdgeOf: nameLabel, offset: 10)
|
||||
valueLabel.autoPinTrailingToSuperviewMargin()
|
||||
valueLabel.autoPinHeightToSuperview()
|
||||
|
||||
stackView.addArrangedSubview(row)
|
||||
}
|
||||
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_STREET", comment: "Label for the 'street' field of a contact's address."),
|
||||
address.street)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POBOX", comment: "Label for the 'pobox' field of a contact's address."),
|
||||
address.pobox)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_NEIGHBORHOOD", comment: "Label for the 'neighborhood' field of a contact's address."),
|
||||
address.neighborhood)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_CITY", comment: "Label for the 'city' field of a contact's address."),
|
||||
address.city)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_REGION", comment: "Label for the 'region' field of a contact's address."),
|
||||
address.region)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_POSTCODE", comment: "Label for the 'postcode' field of a contact's address."),
|
||||
address.postcode)
|
||||
tryToAddNameValue(NSLocalizedString("CONTACT_FIELD_ADDRESS_COUNTRY", comment: "Label for the 'country' field of a contact's address."),
|
||||
address.country)
|
||||
|
||||
return stackView
|
||||
}
|
||||
|
||||
// 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)")
|
||||
|
||||
guard let delegate = self.delegate else {
|
||||
owsFail("\(logTag) missing delegate.")
|
||||
return
|
||||
}
|
||||
|
||||
let filteredContactShare = self.filteredContactShare()
|
||||
assert(filteredContactShare.ows_isValid())
|
||||
|
||||
delegate.approveContactShare(self, didApproveContactShare: filteredContactShare)
|
||||
}
|
||||
|
||||
func didPressCancel() {
|
||||
Logger.info("\(logTag) \(#function)")
|
||||
|
||||
guard let delegate = self.delegate else {
|
||||
owsFail("\(logTag) missing delegate.")
|
||||
return
|
||||
}
|
||||
|
||||
delegate.approveContactShare(self, didCancelContactShare: contactShare)
|
||||
}
|
||||
|
||||
func didPressEditName() {
|
||||
Logger.info("\(logTag) \(#function)")
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
63
SignalMessaging/attachments/ContactFieldView.swift
Normal file
63
SignalMessaging/attachments/ContactFieldView.swift
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ContactFieldView: UIView {
|
||||
|
||||
@available(*, unavailable, message: "use other constructor instead.")
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("Unimplemented")
|
||||
}
|
||||
|
||||
public required init(rows: [UIView], hMargin: CGFloat) {
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
self.layoutMargins = .zero
|
||||
self.preservesSuperviewLayoutMargins = false
|
||||
|
||||
addRows(rows: rows, hMargin: hMargin)
|
||||
}
|
||||
|
||||
private func addRows(rows: [UIView], hMargin: CGFloat) {
|
||||
|
||||
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)
|
||||
self.addSubview(row)
|
||||
row.autoSetDimension(.height, toSize: 1)
|
||||
row.autoPinLeadingToSuperviewMargin(withInset: hMargin)
|
||||
row.autoPinTrailingToSuperviewMargin()
|
||||
row.autoPinEdge(.top, to: .bottom, of: prevRow, withOffset: 0)
|
||||
lastRow = row
|
||||
}
|
||||
|
||||
let addRow: ((UIView) -> Void) = { (row) in
|
||||
if lastRow != nil {
|
||||
addSpacerRow()
|
||||
}
|
||||
self.addSubview(row)
|
||||
row.autoPinLeadingToSuperviewMargin(withInset: hMargin)
|
||||
row.autoPinTrailingToSuperviewMargin(withInset: hMargin)
|
||||
if let lastRow = lastRow {
|
||||
row.autoPinEdge(.top, to: .bottom, of: lastRow, withOffset: 0)
|
||||
} else {
|
||||
row.autoPinEdge(toSuperviewEdge: .top, withInset: 0)
|
||||
}
|
||||
lastRow = row
|
||||
}
|
||||
|
||||
for row in rows {
|
||||
addRow(row)
|
||||
}
|
||||
|
||||
lastRow?.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,313 @@
|
||||
//
|
||||
// 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.autoPinEdgesToSuperviewMargins()
|
||||
|
||||
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()
|
||||
|
||||
scrollView.addSubview(fieldsView)
|
||||
fieldsView.autoPinLeadingToSuperviewMargin()
|
||||
fieldsView.autoPinTrailingToSuperviewMargin()
|
||||
fieldsView.autoPinEdge(toSuperviewEdge: .top)
|
||||
fieldsView.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
}
|
||||
|
||||
private func createFieldsView() -> UIView {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
var rows = [UIView]()
|
||||
|
||||
for fieldView in fieldViews {
|
||||
rows.append(fieldView)
|
||||
}
|
||||
|
||||
return ContactFieldView(rows: rows, hMargin: hMargin)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
guard let navigationController = self.navigationController else {
|
||||
owsFail("\(logTag) Missing navigationController.")
|
||||
return
|
||||
}
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
|
||||
func didPressCancel() {
|
||||
Logger.info("\(logTag) \(#function)")
|
||||
|
||||
guard let navigationController = self.navigationController else {
|
||||
owsFail("\(logTag) Missing navigationController.")
|
||||
return
|
||||
}
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
|
||||
// MARK: - ContactNameFieldViewDelegate
|
||||
|
||||
public func nameFieldDidChange() {
|
||||
updateNavigationBar()
|
||||
}
|
||||
}
|
||||
@ -34,6 +34,7 @@ class ContactsFrameworkContactStoreAdaptee: ContactStoreAdaptee {
|
||||
CNContactThumbnailImageDataKey as CNKeyDescriptor, // TODO full image instead of thumbnail?
|
||||
CNContactPhoneNumbersKey as CNKeyDescriptor,
|
||||
CNContactEmailAddressesKey as CNKeyDescriptor,
|
||||
CNContactPostalAddressesKey as CNKeyDescriptor,
|
||||
CNContactViewController.descriptorForRequiredKeys()
|
||||
]
|
||||
|
||||
|
||||
@ -134,6 +134,10 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
|
||||
{
|
||||
OWSAssert(phoneNumber);
|
||||
|
||||
if (![phoneNumber hasPrefix:COUNTRY_CODE_PREFIX]) {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
PhoneNumber *_Nullable parsedPhoneNumber = [self tryParsePhoneNumberFromE164:phoneNumber];
|
||||
if (!parsedPhoneNumber) {
|
||||
DDLogWarn(@"%@ could not parse phone number.", self.logTag);
|
||||
@ -356,6 +360,9 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
|
||||
|
||||
+ (PhoneNumber *)tryParsePhoneNumberFromE164:(NSString *)text {
|
||||
OWSAssert(text != nil);
|
||||
if (![text hasPrefix:COUNTRY_CODE_PREFIX]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [self phoneNumberFromE164:text];
|
||||
}
|
||||
|
||||
@ -24,13 +24,7 @@ typedef NS_ENUM(NSUInteger, OWSContactPhoneType) {
|
||||
|
||||
NSString *NSStringForContactPhoneType(OWSContactPhoneType value);
|
||||
|
||||
@interface OWSContactPhoneNumber : MTLModel
|
||||
|
||||
@property (nonatomic, readonly) OWSContactPhoneType phoneType;
|
||||
// Applies in the OWSContactPhoneType_Custom case.
|
||||
@property (nonatomic, readonly, nullable) NSString *label;
|
||||
|
||||
@property (nonatomic, readonly) NSString *phoneNumber;
|
||||
@protocol OWSContactField <NSObject>
|
||||
|
||||
- (BOOL)ows_isValid;
|
||||
|
||||
@ -42,6 +36,18 @@ NSString *NSStringForContactPhoneType(OWSContactPhoneType value);
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface OWSContactPhoneNumber : MTLModel <OWSContactField>
|
||||
|
||||
@property (nonatomic, readonly) OWSContactPhoneType phoneType;
|
||||
// Applies in the OWSContactPhoneType_Custom case.
|
||||
@property (nonatomic, readonly, nullable) NSString *label;
|
||||
|
||||
@property (nonatomic, readonly) NSString *phoneNumber;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSContactEmailType) {
|
||||
OWSContactEmailType_Home = 1,
|
||||
OWSContactEmailType_Mobile,
|
||||
@ -51,7 +57,7 @@ typedef NS_ENUM(NSUInteger, OWSContactEmailType) {
|
||||
|
||||
NSString *NSStringForContactEmailType(OWSContactEmailType value);
|
||||
|
||||
@interface OWSContactEmail : MTLModel
|
||||
@interface OWSContactEmail : MTLModel <OWSContactField>
|
||||
|
||||
@property (nonatomic, readonly) OWSContactEmailType emailType;
|
||||
// Applies in the OWSContactEmailType_Custom case.
|
||||
@ -59,12 +65,6 @@ NSString *NSStringForContactEmailType(OWSContactEmailType value);
|
||||
|
||||
@property (nonatomic, readonly) NSString *email;
|
||||
|
||||
- (BOOL)ows_isValid;
|
||||
|
||||
- (NSString *)localizedLabel;
|
||||
|
||||
- (NSString *)debugDescription;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@ -77,7 +77,7 @@ typedef NS_ENUM(NSUInteger, OWSContactAddressType) {
|
||||
|
||||
NSString *NSStringForContactAddressType(OWSContactAddressType value);
|
||||
|
||||
@interface OWSContactAddress : MTLModel
|
||||
@interface OWSContactAddress : MTLModel <OWSContactField>
|
||||
|
||||
@property (nonatomic, readonly) OWSContactAddressType addressType;
|
||||
// Applies in the OWSContactAddressType_Custom case.
|
||||
@ -91,12 +91,6 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
|
||||
@property (nonatomic, readonly, nullable) NSString *postcode;
|
||||
@property (nonatomic, readonly, nullable) NSString *country;
|
||||
|
||||
- (BOOL)ows_isValid;
|
||||
|
||||
- (NSString *)localizedLabel;
|
||||
|
||||
- (NSString *)debugDescription;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@ -128,6 +122,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 -
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
#import "OWSContact+Private.h"
|
||||
#import "OWSSignalServiceProtos.pb.h"
|
||||
#import "PhoneNumber.h"
|
||||
|
||||
//#import "Contact.h"
|
||||
#import "TSAttachment.h"
|
||||
#import <YapDatabase/YapDatabaseTransaction.h>
|
||||
|
||||
@ -45,8 +47,8 @@ NSString *NSStringForContactPhoneType(OWSContactPhoneType value)
|
||||
|
||||
- (BOOL)ows_isValid
|
||||
{
|
||||
if (![PhoneNumber tryParsePhoneNumberFromE164:self.phoneNumber]) {
|
||||
DDLogWarn(@"%@ invalid phone number; not e164: %@.", self.logTag, self.phoneNumber);
|
||||
if (self.phoneNumber.ows_stripped.length < 1) {
|
||||
DDLogWarn(@"%@ invalid phone number: %@.", self.logTag, self.phoneNumber);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
@ -122,7 +124,7 @@ NSString *NSStringForContactEmailType(OWSContactEmailType value)
|
||||
DDLogWarn(@"%@ invalid email: %@.", self.logTag, self.email);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)localizedLabel
|
||||
@ -201,7 +203,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
DDLogWarn(@"%@ invalid address; empty.", self.logTag);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)localizedLabel
|
||||
@ -360,6 +362,13 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDisplayName
|
||||
{
|
||||
_displayName = nil;
|
||||
|
||||
[self ensureDisplayName];
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription
|
||||
{
|
||||
NSMutableString *result = [NSMutableString new];
|
||||
@ -398,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 -
|
||||
|
||||
Loading…
Reference in New Issue
Block a user