|  |  |  | // | 
					
						
							|  |  |  | //  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() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private let hMargin = CGFloat(16) | 
					
						
							|  |  |  |     private let vMargin = CGFloat(10) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     func createContents() { | 
					
						
							|  |  |  |         self.layoutMargins = UIEdgeInsets(top: vMargin, left: hMargin, bottom: vMargin, right: hMargin) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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: ContactShareViewModel) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MARK: - | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @objc | 
					
						
							|  |  |  | public class EditContactShareNameViewController: OWSViewController, ContactNameFieldViewDelegate { | 
					
						
							|  |  |  |     weak var delegate: EditContactShareNameViewControllerDelegate? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let contactShare: ContactShareViewModel | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var namePrefixView: ContactNameFieldView! | 
					
						
							|  |  |  |     var givenNameView: ContactNameFieldView! | 
					
						
							|  |  |  |     var middleNameView: ContactNameFieldView! | 
					
						
							|  |  |  |     var familyNameView: ContactNameFieldView! | 
					
						
							|  |  |  |     var nameSuffixView: ContactNameFieldView! | 
					
						
							|  |  |  |     var organizationNameView: 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: ContactShareViewModel, 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.name.namePrefix, delegate: self) | 
					
						
							|  |  |  |         givenNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_GIVEN_NAME", | 
					
						
							|  |  |  |                                                                      comment: "Label for the 'given name' field of a contact."), | 
					
						
							|  |  |  |                                              value: contactShare.name.givenName, delegate: self) | 
					
						
							|  |  |  |         middleNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_MIDDLE_NAME", | 
					
						
							|  |  |  |                                                                       comment: "Label for the 'middle name' field of a contact."), | 
					
						
							|  |  |  |                                               value: contactShare.name.middleName, delegate: self) | 
					
						
							|  |  |  |         familyNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_FAMILY_NAME", | 
					
						
							|  |  |  |                                                                       comment: "Label for the 'family name' field of a contact."), | 
					
						
							|  |  |  |                                               value: contactShare.name.familyName, delegate: self) | 
					
						
							|  |  |  |         nameSuffixView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_NAME_SUFFIX", | 
					
						
							|  |  |  |                                                                       comment: "Label for the 'name suffix' field of a contact."), | 
					
						
							|  |  |  |                                               value: contactShare.name.nameSuffix, delegate: self) | 
					
						
							|  |  |  |         organizationNameView = ContactNameFieldView(name: NSLocalizedString("CONTACT_FIELD_ORGANIZATION", | 
					
						
							|  |  |  |                                                                             comment: "Label for the 'organization' field of a contact."), | 
					
						
							|  |  |  |                                               value: contactShare.name.organizationName, delegate: self) | 
					
						
							|  |  |  |         fieldViews = [ | 
					
						
							|  |  |  |             namePrefixView , | 
					
						
							|  |  |  |             givenNameView , | 
					
						
							|  |  |  |             middleNameView , | 
					
						
							|  |  |  |             familyNameView , | 
					
						
							|  |  |  |             nameSuffixView, | 
					
						
							|  |  |  |             organizationNameView | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         guard let newName = OWSContactName() else { | 
					
						
							|  |  |  |             owsFail("\(logTag) could not create a new name.") | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         newName.namePrefix = namePrefixView.value().ows_stripped() | 
					
						
							|  |  |  |         newName.givenName = givenNameView.value().ows_stripped() | 
					
						
							|  |  |  |         newName.middleName = middleNameView.value().ows_stripped() | 
					
						
							|  |  |  |         newName.familyName = familyNameView.value().ows_stripped() | 
					
						
							|  |  |  |         newName.nameSuffix = nameSuffixView.value().ows_stripped() | 
					
						
							|  |  |  |         newName.organizationName = organizationNameView.value().ows_stripped() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let modifiedContactShare = contactShare.copy(withName: newName) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |