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

[NT-357][NT-358] Change Payment Method UI #887

Merged
merged 20 commits into from
Oct 16, 2019
Merged

Conversation

justinswart
Copy link
Contributor

📲 What

An unfortunately large PR that adds Change payment method UI to PledgeViewController.

Note: This is UI-only, we'll add the mutation request in a subsequent PR, as well as the ability to update Apple Pay.

🤔 Why

Allows us to update the payment method for a pledge.

🛠 How

This repurposes the PledgeViewController to configure its views based on a third PledgeViewContext, .changedPaymentMethod. It also removes the pledge button from PledgePaymentMethodsViewController so that there is a single submitButton which triggers different events based on the context. We could consider doing this for the continue button too.

  • Added changePaymentMethod case to PledgeViewContext enum.
  • Added convenience vars to PledgeViewContext which can be more easily map'd over in PledgeViewModel.
  • Tests for PledgeViewContext.
  • Tests for PledgeViewModel for the new context.

👀 See

✅ Acceptance criteria

Navigate to Manage Pledge on a project that you've backed, select Change payment method from the context menu.

  • You should see the amount that you backed.
  • If the reward had shipping enabled, this should be shown below the amount.
  • The total should be shown and the T&Cs.
  • The payment methods scrollview should be visible and you should be able to select a new payment method.
  • The confirm button should enable/disable based on whether you've selected the existing payment method for the reward. Note: It appears this isn't currently working because the comparison needs to be made using a GraphID (will fix).

⏰ TODO

  • Wire up submitButton to perform the updateBacking mutation in a subsequent PR.
  • Wire up ability to update Apple Pay payment.

# Conflicts:
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_de_device_pad@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_de_device_phone4_7inch@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_es_device_pad@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_es_device_phone4_7inch@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_fr_device_pad@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_fr_device_phone4_7inch@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_ja_device_pad@2x.png
#	Screenshots/_64/Kickstarter_Framework_iOSTests.PledgeViewControllerTests/testView_UpdateContext_lang_ja_device_phone4_7inch@2x.png
import Prelude_UIKit
import UIKit

final class PledgeAmountSummaryViewController: UIViewController {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just copy-pasting the required views from ManagePledgeSummaryView.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed on Zoom, I'll create a follow-up PR to incorporate this as as subview of ManagePledgeSummaryViewController to promote reuse.

@@ -68,19 +59,12 @@ final class PledgePaymentMethodsViewController: UIViewController {
action: #selector(PledgePaymentMethodsViewController.applePayButtonTapped),
for: .touchUpInside
)

self.pledgeButton.addTarget(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is replaced by submitButton in PledgeViewController.

@@ -60,6 +59,10 @@ final class PledgeViewController: UIViewController, MessageBannerViewControllerP

internal var messageBannerViewController: MessageBannerViewController?

private lazy var pledgeAmountSummaryViewController: PledgeAmountSummaryViewController = {
PledgeAmountSummaryViewController.instantiate()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not the most amazing name for this VC, open to suggestions.

case update
case changePaymentMethod

var confirmationLabelHidden: Bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These booleans basically map directly in PledgeViewModel, it just helped for readability to do this here. They're tested in both places.

paymentSourceValid: Bool,
context: PledgeViewContext
) -> Bool {
if context.isUpdating {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In an updating context we enable the submit button if anything has changed and his valid. In a creating context we require all values to be valid.

Library/ViewModels/PledgeViewModelTests.swift Outdated Show resolved Hide resolved
@ksr-ci-bot
Copy link

1 Warning
⚠️ Big PR

Generated by 🚫 Danger

@@ -392,8 +392,6 @@ internal final class AddNewCardViewModelTests: TestCase {
}

func testUnsupportedCardMessage_hidesWithValidCardBrand_AndExistingCardNumber_withSettingsIntent() {
let project = Project.template
Copy link
Contributor

Choose a reason for hiding this comment

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

Ty 🙏

self.vm.inputs.viewDidLoad()

self.goToChangePaymentMethod.assertDidNotEmitValue()
self.goToChangePaymentMethodProject.assertDidNotEmitValue()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add aself.goToChangePaymentMethodReward.assertDidNotEmitValue() here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, thanks!

self.shippingAmountText = project
.combineLatest(with: shippingAmount)
.map { project, shippingAmount in
shippingValue(with: project, with: shippingAmount)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm this function name looks a bit strange. Maybe shippingValue(with project: Project, shippingAmount: Double)?

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, sure, this was a copy-paste 😄

)
.map(first)

let backing = project
Copy link
Contributor

Choose a reason for hiding this comment

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

Something you might consider doing is configuring this view with a project and a backing:

func configureWith(project: Project, backing: Backing)

It would simplify a few things in this view model.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think seeing as the Backing is from the Project I'm going to leave it as is for now.

let _ = backing.locationName else {
return true
}
if let reward = backing.reward {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is interesting. Do you think we need this extra layer of logic? I'd imagine if the backing.locationName and backing.locationId are not nil that you could assume that the backing was created with a shipping address. 🤔

I wonder if this could cause a bug in a situation where the reward's shipping eligibility is modified by the creator after the user has created a pledge (not sure if that's even possible).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another copy-paste, I'll check it out, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirmed with team R&F that this cannot occur, so checking that locationId is not nil on the Backing is sufficient. Good catch!

Copy link
Contributor

@dusi dusi left a comment

Choose a reason for hiding this comment

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

Had a first run through this and mostly no red flags..

A11y

Do we want the space here? I believe our formatter returns empty nbsp space at the beginning of the string which is fairly visible when the stack view is aligned vertically.

Screen Shot 2019-10-11 at 2 52 05 PM

Changing font size crashes the screen (even after adding the following workaround)...wonder what's the real problem here.

DispatchQueue.main.async {
  self.viewModel.inputs.traitCollectionDidChange()
}


_ = self.pledgeAmountStackView
|> checkoutAdaptableStackViewStyle(
self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we declare local variable and reuse it (since we're using it twice here)?

let isAccessibilityCategory = self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory
...
checkoutAdaptableStackViewStyle(isAccessibilityCategory)


private let amountLabelStyle: LabelStyle = { label in
label
|> \.adjustsFontForContentSizeCategory .~ true
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also need to set font? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pledgeAmountLabel uses an attributed string so the font setting isn't necessary here.

@@ -182,20 +153,12 @@ final class PledgePaymentMethodsViewController: UIViewController {
self.viewModel.inputs.configureWith(pledgePaymentMethodsValue)
}

// MARK: - Accessors
// MARK: - Actions
Copy link
Contributor

Choose a reason for hiding this comment

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

nice

@@ -11,10 +11,9 @@ protocol PledgeViewControllerDelegate: AnyObject {
final class PledgeViewController: UIViewController, MessageBannerViewControllerPresenting {
// MARK: - Properties

private lazy var confirmButton: UIButton = { UIButton(type: .custom) }()
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah this will really mess up my loading button but it should be fairly simple to fix

@@ -75,8 +78,14 @@ final class PledgeViewController: UIViewController, MessageBannerViewControllerP
|> \.delegate .~ self
}()

private lazy var submitButton: UIButton = { UIButton(type: .custom) }()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is init with type the designated initializer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah we want this to be a custom button to not have the highlight animation that system buttons get.

@@ -40,7 +36,8 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT
let configureWithValue = Signal.combineLatest(
self.viewDidLoadProperty.signal,
self.configureWithValueProperty.signal.skipNil()
).map(second)
)
.map(second)
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@dusi dusi left a comment

Choose a reason for hiding this comment

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

Noticed some minor thing

and also this issue - is the total value calculated properly for the first couple Snapshot tests? testView_ChangePaymentMethodContext?

Screen Shot 2019-10-15 at 12 26 07 PM

super.viewDidLoad()

self.configureSubviews()
self.bindViewModel()
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this happening through our swizzle thingys? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, thanks.

@justinswart
Copy link
Contributor Author

@dusi Good catch on the snapshots - This is because the template Reward's ShippingRule had an amount of 5 but the Backing on the Project had a shipping amount of 10. I think this is ok because in reality when we're changing the payment method these things should line up, but let's test this logic a bit more to be sure.

Copy link
Contributor

@dusi dusi left a comment

Choose a reason for hiding this comment

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

I can approve this if @ifbarrera is fine with this!

🚢 🚢 🚢
🚢 🚢 🚢
🚢 🚢 🚢

* Wire up change payment method to mutation for payment source and Apple Pay

* Remove payment method when backing with Apple Pay and vice versa

* Update tests for apple pay sheet race condition fixes

* Replace zip with simpler signals

* Add Stripe token error test

* Move signals around, improve tests
@justinswart justinswart merged commit 7de27fa into master Oct 16, 2019
@justinswart justinswart deleted the change-payment-method-ui branch July 14, 2021 21:20
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.

4 participants