-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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-303] Pledge Button #847
Changes from all commits
24e38bf
2b61c20
519c140
05e1bbf
1961fbb
36d102d
4fdecfd
86db0fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,18 +3,25 @@ import Library | |
import Prelude | ||
import UIKit | ||
|
||
protocol PledgeCreditCardViewDelegate: AnyObject { | ||
func pledgeCreditCardViewSelected( | ||
_ pledgeCreditCardView: PledgeCreditCardView, | ||
paymentSourceId: String | ||
) | ||
} | ||
|
||
final class PledgeCreditCardView: UIView { | ||
// MARK: - Properties | ||
|
||
private let viewModel: CreditCardCellViewModelType = CreditCardCellViewModel() | ||
|
||
private let adaptableStackView: UIStackView = { UIStackView(frame: .zero) }() | ||
weak var delegate: PledgeCreditCardViewDelegate? | ||
private let expirationDateLabel: UILabel = { UILabel(frame: .zero) }() | ||
private let imageView: UIImageView = { UIImageView(frame: .zero) }() | ||
private let labelsStackView: UIStackView = { UIStackView(frame: .zero) }() | ||
private let lastFourLabel: UILabel = { UILabel(frame: .zero) }() | ||
private let rootStackView: UIStackView = { UIStackView(frame: .zero) }() | ||
private let selectButton: UIButton = { UIButton(type: .custom) }() | ||
private let viewModel: CreditCardCellViewModelType = CreditCardCellViewModel() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mentioned in #835 that we should probably rename this 😬 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I agree - I really don't think we should have used the same view model as the one in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I had no idea it was being used in both places. |
||
|
||
// MARK: - Lifecycle | ||
|
||
|
@@ -46,6 +53,11 @@ final class PledgeCreditCardView: UIView { | |
_ = (self.rootStackView, self) | ||
|> ksr_addSubviewToParent() | ||
|> ksr_constrainViewToMarginsInParent() | ||
|
||
self.selectButton.addTarget( | ||
self, action: #selector(PledgeCreditCardView.selectButtonTapped), | ||
for: .touchUpInside | ||
) | ||
} | ||
|
||
private func setupConstraints() { | ||
|
@@ -101,11 +113,25 @@ final class PledgeCreditCardView: UIView { | |
_ = self?.imageView | ||
?|> \.image .~ image | ||
} | ||
|
||
self.viewModel.outputs.notifyDelegateOfCardSelected | ||
.observeForUI() | ||
.observeValues { [weak self] paymentSourceId in | ||
guard let self = self else { return } | ||
|
||
self.delegate?.pledgeCreditCardViewSelected(self, paymentSourceId: paymentSourceId) | ||
} | ||
} | ||
|
||
func configureWith(value: GraphUserCreditCard.CreditCard) { | ||
self.viewModel.inputs.configureWith(creditCard: value) | ||
} | ||
|
||
// MARK: - Accessors | ||
|
||
@objc func selectButtonTapped() { | ||
self.viewModel.inputs.selectButtonTapped() | ||
} | ||
} | ||
|
||
// MARK: - Styles | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,10 @@ protocol PledgePaymentMethodsViewControllerDelegate: AnyObject { | |
func pledgePaymentMethodsViewControllerDidTapApplePayButton( | ||
_ viewController: PledgePaymentMethodsViewController | ||
) | ||
func pledgePaymentMethodsViewController( | ||
_ viewController: PledgePaymentMethodsViewController, | ||
didSelectCreditCard paymentSourceId: String | ||
) | ||
} | ||
|
||
final class PledgePaymentMethodsViewController: UIViewController { | ||
|
@@ -17,6 +21,7 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
private lazy var cardsStackView: UIStackView = { UIStackView(frame: .zero) }() | ||
internal weak var delegate: PledgePaymentMethodsViewControllerDelegate? | ||
internal weak var messageDisplayingDelegate: PledgeViewControllerMessageDisplaying? | ||
private lazy var pledgeButton: UIButton = { UIButton.init(type: .custom) }() | ||
private lazy var rootStackView: UIStackView = { UIStackView(frame: .zero) }() | ||
private lazy var scrollView: UIScrollView = { UIScrollView(frame: .zero) }() | ||
private lazy var scrollViewContainer: UIView = { UIView(frame: .zero) }() | ||
|
@@ -44,7 +49,10 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
|> ksr_addSubviewToParent() | ||
|> ksr_constrainViewToEdgesInParent() | ||
|
||
_ = ([self.applePayButton, self.spacer, self.titleLabel, self.scrollViewContainer], self.rootStackView) | ||
_ = ( | ||
[self.applePayButton, self.spacer, self.titleLabel, self.scrollViewContainer, self.pledgeButton], | ||
self.rootStackView | ||
) | ||
|> ksr_addArrangedSubviewsToStackView() | ||
|
||
_ = (self.rootStackView, self.view) | ||
|
@@ -61,7 +69,8 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
private func setupConstraints() { | ||
NSLayoutConstraint.activate([ | ||
self.cardsStackView.heightAnchor.constraint(equalTo: self.scrollViewContainer.heightAnchor), | ||
self.applePayButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.minTouchSize.height) | ||
self.applePayButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.minTouchSize.height), | ||
self.pledgeButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.minTouchSize.height) | ||
]) | ||
} | ||
|
||
|
@@ -78,6 +87,10 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
_ = self.applePayButton | ||
|> applePayButtonStyle | ||
|
||
_ = self.pledgeButton | ||
|> greenButtonStyle | ||
|> UIButton.lens.title(for: .normal) %~ { _ in Strings.Pledge() } | ||
|
||
_ = self.scrollView | ||
|> checkoutBackgroundStyle | ||
|
||
|
@@ -115,7 +128,16 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
self.delegate?.pledgePaymentMethodsViewControllerDidTapApplePayButton(self) | ||
} | ||
|
||
self.viewModel.outputs.notifyDelegateCreditCardSelected | ||
.observeForUI() | ||
.observeValues { [weak self] paymentSourceId in | ||
guard let self = self else { return } | ||
|
||
self.delegate?.pledgePaymentMethodsViewController(self, didSelectCreditCard: paymentSourceId) | ||
} | ||
|
||
self.applePayButton.rac.hidden = self.viewModel.outputs.applePayButtonHidden | ||
self.pledgeButton.rac.enabled = self.viewModel.outputs.pledgeButtonEnabled | ||
} | ||
|
||
// MARK: - Configuration | ||
|
@@ -130,12 +152,16 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
self.viewModel.inputs.configureWith(pledgePaymentMethodsValue) | ||
} | ||
|
||
// MARK: - Actions | ||
// MARK: - Accessors | ||
|
||
@objc private func applePayButtonTapped() { | ||
self.viewModel.inputs.applePayButtonTapped() | ||
} | ||
|
||
func updatePledgeButton(_ enabled: Bool) { | ||
self.viewModel.inputs.updatePledgeButtonEnabled(isEnabled: enabled) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The real "brains" of the pledge button enabled state is actually the |
||
} | ||
|
||
// MARK: - Functions | ||
|
||
private func addCardsToStackView(_ cards: [GraphUserCreditCard.CreditCard]) { | ||
|
@@ -145,6 +171,8 @@ final class PledgePaymentMethodsViewController: UIViewController { | |
.map { card -> PledgeCreditCardView in | ||
let cardView = PledgeCreditCardView(frame: .zero) | ||
cardView.configureWith(value: card) | ||
cardView.delegate = self | ||
|
||
return cardView | ||
} | ||
|
||
|
@@ -201,3 +229,9 @@ extension PledgePaymentMethodsViewController: AddNewCardViewControllerDelegate { | |
// TODO: | ||
} | ||
} | ||
|
||
extension PledgePaymentMethodsViewController: PledgeCreditCardViewDelegate { | ||
func pledgeCreditCardViewSelected(_: PledgeCreditCardView, paymentSourceId: String) { | ||
self.viewModel.creditCardSelected(paymentSourceId: paymentSourceId) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,13 +8,17 @@ public typealias PledgePaymentMethodsValue = (user: User, project: Project, appl | |
public protocol PledgePaymentMethodsViewModelInputs { | ||
func applePayButtonTapped() | ||
func configureWith(_ value: PledgePaymentMethodsValue) | ||
func creditCardSelected(paymentSourceId: String) | ||
func updatePledgeButtonEnabled(isEnabled: Bool) | ||
func viewDidLoad() | ||
} | ||
|
||
public protocol PledgePaymentMethodsViewModelOutputs { | ||
var notifyDelegateApplePayButtonTapped: Signal<Void, Never> { get } | ||
var applePayButtonHidden: Signal<Bool, Never> { get } | ||
var notifyDelegateApplePayButtonTapped: Signal<Void, Never> { get } | ||
var notifyDelegateCreditCardSelected: Signal<String, Never> { get } | ||
var notifyDelegateLoadPaymentMethodsError: Signal<String, Never> { get } | ||
var pledgeButtonEnabled: Signal<Bool, Never> { get } | ||
var reloadPaymentMethods: Signal<[GraphUserCreditCard.CreditCard], Never> { get } | ||
} | ||
|
||
|
@@ -44,6 +48,11 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT | |
.map(showApplePayButton(for:applePayCapable:)) | ||
.negate() | ||
|
||
self.pledgeButtonEnabled = Signal.merge( | ||
configureWithValue.mapConst(false), | ||
self.pledgeButtonEnabledSignal | ||
).skipRepeats() | ||
|
||
self.reloadPaymentMethods = storedCardsEvent | ||
.values() | ||
.map { $0.me.storedCards.nodes } | ||
|
@@ -53,6 +62,9 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT | |
self.notifyDelegateLoadPaymentMethodsError = storedCardsEvent | ||
.errors() | ||
.map { $0.localizedDescription } | ||
|
||
self.notifyDelegateCreditCardSelected = self.creditCardSelectedSignal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now this is very simple - has a credit card been selected? An upcoming PR will add the rest of the requirements for the pledge button being enabled: reward minimum is met, shipping rule selected, etc. |
||
.skipRepeats() | ||
} | ||
|
||
private let applePayButtonTappedProperty = MutableProperty(()) | ||
|
@@ -65,6 +77,16 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT | |
self.configureWithValueProperty.value = value | ||
} | ||
|
||
private let (creditCardSelectedSignal, creditCardSelectedObserver) = Signal<String, Never>.pipe() | ||
public func creditCardSelected(paymentSourceId: String) { | ||
self.creditCardSelectedObserver.send(value: paymentSourceId) | ||
} | ||
|
||
private let (pledgeButtonEnabledSignal, pledgeButtonEnabledObserver) = Signal<Bool, Never>.pipe() | ||
public func updatePledgeButtonEnabled(isEnabled: Bool) { | ||
self.pledgeButtonEnabledObserver.send(value: isEnabled) | ||
} | ||
|
||
private let viewDidLoadProperty = MutableProperty(()) | ||
public func viewDidLoad() { | ||
self.viewDidLoadProperty.value = () | ||
|
@@ -75,7 +97,9 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT | |
|
||
public let notifyDelegateApplePayButtonTapped: Signal<Void, Never> | ||
public let applePayButtonHidden: Signal<Bool, Never> | ||
public let notifyDelegateCreditCardSelected: Signal<String, Never> | ||
public let notifyDelegateLoadPaymentMethodsError: Signal<String, Never> | ||
public let pledgeButtonEnabled: Signal<Bool, Never> | ||
public let reloadPaymentMethods: Signal<[GraphUserCreditCard.CreditCard], Never> | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might overlap with some of the work happening in #835.