Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Order Details: Customer Info #53

Merged
merged 27 commits into from
May 11, 2018
Merged

Conversation

mindgraffiti
Copy link
Contributor

@mindgraffiti mindgraffiti commented May 7, 2018

Task #11

To Do

  • Change customer info section to use MVVM

==================================

Display an Order Details view with the customer information section. Note that the Shipping section does not support a phone number property. See API reference.

screen shot 2018-04-05 at 8 40 16 am

To Do

  • Customer information section title
  • Localize addresses
  • Localize phone numbers
  • Validate phone numbers

Shipping

  • Shipping details title. Display data: customer name, full address

Billing

  • Billing details title. Display data: customer name, full address
  • billing phone number, "more" button icon
  • "more" action sheet: Call, Message
  • billing email, email button
  • email button (action): email message presenter
  • Show/hide billing (button)
  • Show/hide billing (action)

@mindgraffiti mindgraffiti added type: question Primarily a question about how code works, etc. Size: L labels May 7, 2018
@mindgraffiti mindgraffiti self-assigned this May 7, 2018
@mindgraffiti mindgraffiti added this to Icebox in MVLP Kanban Board via automation May 7, 2018

extension OrderDetailsCustomerInfoCell {
func configure(with viewModel: OrderDetailsViewModel) {
title = "hi" // what now? How to tell between shipping or billing display?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far every viewModel I have implemented has been a 1:1 representation. But the customer info cell gets used for both shipping info and billing info. How do I detect which data to pull from the viewModel?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there @mindgraffiti !!

Since the Order actually has two Addresses, we'd probably need something like AddressViewModel. The Order would have references to the billing / shipping, and you could do...

func configure(with viewModel: AddressViewModel) {  }

(Which would hopefully solve the ambiguity issue!)

@mindgraffiti
Copy link
Contributor Author

Hi @jleandroperez, could I bother you with a question about the view model? This PR is not ready for review. It is here for context on the question :)

@mindgraffiti mindgraffiti moved this from Icebox to In Progress in MVLP Kanban Board May 9, 2018
@astralbodies astralbodies moved this from In Progress to Review/Testing in MVLP Kanban Board May 9, 2018
@mindgraffiti mindgraffiti removed the type: question Primarily a question about how code works, etc. label May 9, 2018
let emailButton = UIButton(type: .custom)
emailButton.frame = CGRect(x: 8, y: 0, width: 44, height: 44)
emailButton.setImage(Gridicon.iconOfType(.mail), for: .normal)
emailButton.contentHorizontalAlignment = .right

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Natural Content Alignment Violation: Forcing content alignment left or right can affect the Right-to-Left layout. Use naturalContentHorizontalAlignment instead. (natural_content_alignment)

let phoneButton = UIButton(type: .custom)
phoneButton.frame = CGRect(x: 8, y: 0, width: 44, height: 44)
phoneButton.setImage(Gridicon.iconOfType(.ellipsis), for: .normal)
phoneButton.contentHorizontalAlignment = .right

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Natural Content Alignment Violation: Forcing content alignment left or right can affect the Right-to-Left layout. Use naturalContentHorizontalAlignment instead. (natural_content_alignment)

contact.givenName = address.firstName
contact.familyName = address.lastName

if let organization = address.company {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opening Brace Spacing Violation: Opening braces should be preceded by a single space and on the same line as the declaration. (opening_brace)

@mindgraffiti
Copy link
Contributor Author

Hi @jleandroperez! Would you please review this one? No more questions, it's finally ready!

Copy link
Collaborator

@jleandroperez jleandroperez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello there @mindgraffiti !! looks good!!. First review pass.

Let's sync over slack on alternatives to approach UITableViewDataSource!!

let infoNib = UINib(nibName: OrderDetailsCustomerInfoCell.reuseIdentifier, bundle: nil)
tableView.register(infoNib, forCellReuseIdentifier: OrderDetailsCustomerInfoCell.reuseIdentifier)
let footerNib = UINib(nibName: ShowHideFooterCell.reuseIdentifier, bundle: nil)
tableView.register(footerNib, forHeaderFooterViewReuseIdentifier: ShowHideFooterCell.reuseIdentifier)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion to reduce code duplication:

let identifiers = [OrderDetailsSummaryCell.reuseIdentifier, OrderDetailsCustomerNoteCell.reuseIdentifier, OrderDetailsCustomerInfoCell.reuseIdentifier, ShowHideFooterCell.reuseIdentifier[

for identifier in identifiers {
    let nib = UINib(nibName: identifier, bundle: nil)
    tableView.register(footerNib, forHeaderFooterViewReuseIdentifier: identifier)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

}
set {
titleLabel.text = newValue
titleLabel.applyTitleStyle()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyTitleStyle() sets both, the font and color. Since that doesn't depend on the text, can we move that elsewhere?

@IBOutlet private weak var titleLabel: UILabel! {
   didSet {
      titleLabel.applyTitleStyle()
   }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

}
set {
nameLabel.text = newValue
nameLabel.applyBodyStyle()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note as above: can we move applyBodyStyle to nameLabel's didSet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

}
set {
addressLabel.text = newValue
addressLabel.applyBodyStyle()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note as above: can we move applyBodyStyle to the IBOutlet's didSet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

default:
return UITableViewCell()
}
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if sectionTitles[section].isEmpty {
return 0.0001
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip: CGFloat.leastNonzeroMagnitude

Rule of thumb: never ever ever use magic numbers (those are "constants inline"). You'd usually want to define constants for those.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I knew .zero but didn't know .leastNonzeroMagnitude existed

}
}

extension CNContact {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please move this one to it's own file? (for clarity mostly!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

return cell
}
let zeroFrame = CGRect(x: 0, y: 0, width: 0, height: 0.001)
return UIView(frame: zeroFrame)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Almost sure!!) you can just do return nil here. Did that trigger a bug on your end?

If so... a cute way to do this is with just return UIView(frame: .zero)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so the bug is in -heightForFooterSection, where it has to return a tiny footer height in order to collapse empty section footers. Thanks for the reminder, this one should return nil, not the tiny UIView.

}

func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if section == Section.info.rawValue {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip tip:

  • You'd normally wanna avoid code indentation (Since it's harder to read / grasp what belongs to what brace).
  • Since there's a single case here, you could do:
guard section == Section.info.rawValue else {
    return nil
}

(And the biggest block gets one less tab)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

phoneButton.tintColor = StyleManager.wooCommerceBrandColor
phoneButton.addTarget(self, action: #selector(phoneButtonAction), for: .touchUpInside)

let iconView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please please move all of the magic numbers to constants? (view frames + reuse identifiers)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

emailButton.tintColor = StyleManager.wooCommerceBrandColor
emailButton.addTarget(self, action: #selector(emailButtonAction), for: .touchUpInside)

let iconView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same comment as above)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)

@mindgraffiti
Copy link
Contributor Author

@jleandroperez thank you for making this code look cute! Ready for another look.

Copy link
Collaborator

@jleandroperez jleandroperez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few nipticky comments!!

var formattedAddress: String
var cleanedPhoneNumber: String?
var phoneNumber: String?
var email: String?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please please: Can we switch everything to let ?

cleanedPhoneNumber = address.phone?.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()

let cnAddress = contact.postalAddresses.first
let postalAddress = cnAddress!.value
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a safe unwrap here, just in case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed formattedAddress to an optional

self?.setShowHideFooter()
// it would be nice to have `tableView.reloadSections([section], with: .bottom)`
// but iOS 11 has an ugly animation bug https://forums.developer.apple.com/thread/86703
tableView.reloadData()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've got this working in WPiOS:

1. Log into a wpcom account (in wpios)
2. Open Me > Notification Settings
3. Press over 'View all...'

(That's the animation you'd like to do here!) Snippet here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh thank you! This looks so much better now.

controller.messageComposeDelegate = self
present(controller, animated: true, completion: nil)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely split this one in two. Reasoning is sendTextMessage doesn't imply the method may or may not do something.

You'd normally cut this this way:

func sendTextMessageIfPossible() { }
private func sendTextMessage() {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I didn't consider the implications. Glad to make it more explicit

controller.mailComposeDelegate = self
present(controller, animated: true, completion: nil)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same as above!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

Copy link
Collaborator

@jleandroperez jleandroperez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful!!! 🎉 :shipit:

phoneButton.addTarget(self, action: #selector(phoneButtonAction), for: .touchUpInside)

let iconView = UIView(frame: Constants.accessoryFrame)
iconView .addSubview(phoneButton)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nipticky: extra space

@mindgraffiti mindgraffiti merged commit df273f2 into develop May 11, 2018
MVLP Kanban Board automation moved this from Review/Testing to Done/Merged May 11, 2018
@mindgraffiti mindgraffiti deleted the issue/11-customer-info-mvvm branch May 11, 2018 20:15
@astralbodies astralbodies mentioned this pull request Jun 27, 2018
9 tasks
@jleandroperez jleandroperez added this to the External closed beta milestone Jun 27, 2018
@jleandroperez jleandroperez changed the title Issue/11 customer info mvvm Order Details: Customer Info Jun 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants