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

Display a “default” badge for the default PM #4333

Merged
merged 86 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 81 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
1403094
reading default_payment_method from back end?
joyceqin-stripe Nov 27, 2024
71c5516
revert accidental space changes;
joyceqin-stripe Nov 27, 2024
e3ecc2c
customersheet default from back end
joyceqin-stripe Nov 27, 2024
fb86faa
removed unused value
joyceqin-stripe Dec 2, 2024
b283446
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 2, 2024
9ed5ed2
remove unused property
joyceqin-stripe Dec 2, 2024
e689449
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 2, 2024
3c36c97
fix build issue
joyceqin-stripe Dec 2, 2024
c749e6e
Merge branch 'joyceqin-MOBILESDK-2799' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 2, 2024
aac6ae1
fix test
joyceqin-stripe Dec 2, 2024
90e15a4
display default badge
joyceqin-stripe Dec 2, 2024
b25eb4b
merged with master
joyceqin-stripe Dec 4, 2024
16e6b0c
use localized string for Default badge
joyceqin-stripe Dec 4, 2024
5936f96
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 4, 2024
5cd59d6
small change for less repetition in code
joyceqin-stripe Dec 4, 2024
54a8073
snapshot tests
joyceqin-stripe Dec 5, 2024
0fca0dc
check default pm against savedPaymentMethods instead of customer.paym…
joyceqin-stripe Dec 5, 2024
c8bd06b
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 5, 2024
c226e09
removed alternateUpdatePaymentMethodNavigation, consolidated reading …
joyceqin-stripe Dec 5, 2024
4d146b6
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 5, 2024
f858536
remove alternateUpdatePaymentMethodNavigation flag and updated tests
joyceqin-stripe Dec 6, 2024
8d1c90b
update snapshot tests
joyceqin-stripe Dec 6, 2024
c70c6a1
update row button chevron condition
joyceqin-stripe Dec 6, 2024
d999b9e
remove removeonly mode, update tests
joyceqin-stripe Dec 6, 2024
ac5189a
fixing tests
joyceqin-stripe Dec 6, 2024
ffcf6e4
remove wait for chevron existence in tests for last card
joyceqin-stripe Dec 6, 2024
403fdcb
localizable string
joyceqin-stripe Dec 6, 2024
c8fbfef
edit a test
joyceqin-stripe Dec 6, 2024
2f515cd
merged with remove alternateupdatepaymentmethodnavigation flag
joyceqin-stripe Dec 6, 2024
0a92c87
remove removeOnly snapshots
joyceqin-stripe Dec 6, 2024
a89890c
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
bd51b31
changelog
joyceqin-stripe Dec 6, 2024
3c69384
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
f239d20
update changelog and tests
joyceqin-stripe Dec 6, 2024
5fad2d3
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
4da3034
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 6, 2024
f83164f
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 6, 2024
05407cc
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
60cd77c
merge with persisted default
joyceqin-stripe Dec 6, 2024
fe79f55
readd default badge snapshot test
joyceqin-stripe Dec 6, 2024
ea5228f
remove dead code
joyceqin-stripe Dec 6, 2024
eaae456
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
623ffe3
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 6, 2024
7022a11
sleep for 1 second to allow label animation to finish testRemovalOfSa…
joyceqin-stripe Dec 6, 2024
f5fbd58
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
05a0a04
fix build
joyceqin-stripe Dec 6, 2024
1794106
remove extra argument
joyceqin-stripe Dec 6, 2024
cb12138
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 6, 2024
efb682f
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 6, 2024
b592b55
fallback when opted in to set as default not local default but first …
joyceqin-stripe Dec 9, 2024
74c9db8
added tests
joyceqin-stripe Dec 9, 2024
a28440e
merge
joyceqin-stripe Dec 9, 2024
b6d712e
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 16, 2024
d4dedf5
merge
joyceqin-stripe Dec 16, 2024
f9d0996
fix non compiling test
joyceqin-stripe Dec 16, 2024
0c52f28
dynamically increase height of collectionview cells when default badg…
joyceqin-stripe Dec 17, 2024
8f30c22
snapshot tests
joyceqin-stripe Dec 17, 2024
d6ea0c6
removed hard coded default value
joyceqin-stripe Dec 17, 2024
04b187d
snapshots
joyceqin-stripe Dec 17, 2024
8d7509c
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 17, 2024
afcfc48
revert verticalspmvc snapshot tests
joyceqin-stripe Dec 17, 2024
b88ce62
Merge branch 'joyceqin-MOBILESDK-2800' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 17, 2024
6dba918
do not change cell size if should not display default badge;
joyceqin-stripe Dec 17, 2024
626f4d5
cleanup vertical default badge code
joyceqin-stripe Dec 17, 2024
734345e
update snapshots
joyceqin-stripe Dec 17, 2024
f6844ec
always set isDefaultPM to true or false
joyceqin-stripe Dec 18, 2024
063fcb1
condense activation deactivation code
joyceqin-stripe Dec 18, 2024
3867417
remove num lines on label
joyceqin-stripe Dec 18, 2024
6670834
fix build
joyceqin-stripe Dec 18, 2024
f05cdcb
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 18, 2024
ff08f77
revert spacing
joyceqin-stripe Dec 18, 2024
53b894a
Merge branch 'joyceqin-MOBILESDK-2800' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 18, 2024
8382a93
cleaned up elementscustomer functions, nonoptional constraints to act…
joyceqin-stripe Dec 18, 2024
22e2b02
refactored names and defined isDefaultPaymentMethod functions
joyceqin-stripe Dec 19, 2024
6bcc425
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 19, 2024
304aa41
fix vertical default badge snapshot;
joyceqin-stripe Dec 19, 2024
9dd0cfb
adjust default badge font weight when selected vs not
joyceqin-stripe Dec 19, 2024
10de2e9
clean up activate and deactivate default badge constraints code
joyceqin-stripe Dec 19, 2024
d27f4c8
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 19, 2024
9d16dec
fix if condition
joyceqin-stripe Dec 19, 2024
bdda2bb
`Merge branch 'joyceqin-MOBILESDK-2800' of github.com:stripe/stripe-i…
joyceqin-stripe Dec 19, 2024
9048ed0
pass badgeText into RowButton instead of defaultBadge label
joyceqin-stripe Dec 19, 2024
dd6dedb
reran snapshots
joyceqin-stripe Dec 19, 2024
238e94e
Merge branch 'master' into joyceqin-MOBILESDK-2800
joyceqin-stripe Dec 19, 2024
9b76fa9
separated var for badgetext
joyceqin-stripe Dec 19, 2024
53a55c8
Merge branch 'joyceqin-MOBILESDK-2800' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -437,4 +437,11 @@ extension String.Localized {
"Promotional text for Affirm, displayed in a button that lets the customer pay with Affirm"
)
}

static var default_text: String {
STPLocalizedString(
"Default",
"Label for identifying the default payment method."
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ struct ElementsCustomer: Equatable, Hashable {
return ElementsCustomer(paymentMethods: paymentMethods, defaultPaymentMethod: defaultPaymentMethod, customerSession: customerSession)
}

func getDefaultPaymentMethod() -> STPPaymentMethod? {
return paymentMethods.first { $0.stripeId == defaultPaymentMethod }
}

func getDefaultOrFirstPaymentMethod() -> STPPaymentMethod? {
Copy link
Collaborator

Choose a reason for hiding this comment

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

in PaymentSheetVerticalViewController, we have this logic for the initialSelection

                    if let customer = elementsSession.customer,
                       let defaultPaymentMethod = customer.getDefaultOrFirstPaymentMethod() {
                        customerDefault = CustomerPaymentOption.stripeId(defaultPaymentMethod.stripeId)
                    }

This seems to indicate that the first payment method can become the default. Is this true? Ultimately, i'm trying to wrap my head around when we would access defaultPaymentMethod vs defaultOrFirst.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

From my understanding, the selected payment method should be the default if it exists or the first payment method in the customer's list of saved payment methods. However, the default badge itself should only be displayed if there is a set default in the elementsSession customer.

So we access defaultOrFirst when trying to determine the selected payment method, while we access defaultPaymentMethod to determine if the default badge should be shown and which payment method to show it on.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah thanks for the explanation!

// if customer has a default payment method from the elements session, return the default payment method
let defaultSavedPaymentMethod = paymentMethods.first { $0.stripeId == defaultPaymentMethod }
if let defaultSavedPaymentMethod = defaultSavedPaymentMethod {
return defaultSavedPaymentMethod
}
// otherwise, return the first payment method from the customer's list of saved payment methods
return paymentMethods.first
return getDefaultPaymentMethod() ?? paymentMethods.first
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ extension LinkPaymentMethodPicker {

private let defaultBadge = LinkBadgeView(
type: .neutral,
text: STPLocalizedString("Default", "Label for identifying the default payment method.")
text: String.Localized.default_text
)

private let alertIconView: UIImageView = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import UIKit
// MARK: - Constants
/// Entire cell size
private let cellSize: CGSize = CGSize(width: 106, height: 94)
private let cellSizeWithDefaultBadge: CGSize = CGSize(width: 106, height: 112)
Comment on lines 18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the harm in making all the cells the new size to account for the possible default badge? What happens if we use the old size? Would be preferred if we can avoid swapping sizes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The main issue there is that in the Figma, the height of the sheet changes when edit mode is on, but if we just increase the size of the cells constantly, then there will just be extra space under the PMs and the height won't change.

/// Size of the rounded rectangle that contains the PM logo
let roundedRectangleSize = CGSize(width: 100, height: 64)
private let paymentMethodLogoSize: CGSize = CGSize(width: 54, height: 40)
Expand All @@ -24,12 +25,13 @@ private let paymentMethodLogoSize: CGSize = CGSize(width: 54, height: 40)
/// For internal SDK use only
@objc(STP_Internal_SavedPaymentMethodCollectionView)
class SavedPaymentMethodCollectionView: UICollectionView {
init(appearance: PaymentSheet.Appearance) {
init(appearance: PaymentSheet.Appearance, needsVerticalPaddingForBadge: Bool = false) {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(
top: -6, left: PaymentSheetUI.defaultPadding, bottom: 0,
right: PaymentSheetUI.defaultPadding)
self.needsVerticalPaddingForBadge = needsVerticalPaddingForBadge
layout.itemSize = cellSize
layout.minimumInteritemSpacing = 12
layout.minimumLineSpacing = 4
Expand All @@ -43,13 +45,23 @@ class SavedPaymentMethodCollectionView: UICollectionView {
}

var isRemovingPaymentMethods: Bool = false
let needsVerticalPaddingForBadge: Bool

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: 100)
return needsVerticalPaddingForBadge && isRemovingPaymentMethods ? CGSize(width: UIView.noIntrinsicMetric, height: 118) : CGSize(width: UIView.noIntrinsicMetric, height: 100)
}

func updateLayout() {
guard let layout = collectionViewLayout as? UICollectionViewFlowLayout else { return }
let newCellSize = needsVerticalPaddingForBadge && isRemovingPaymentMethods ? cellSizeWithDefaultBadge : cellSize
guard newCellSize != layout.itemSize else { return }
layout.itemSize = newCellSize
collectionViewLayout.invalidateLayout()
invalidateIntrinsicContentSize()
}
}

Expand Down Expand Up @@ -91,11 +103,30 @@ extension SavedPaymentMethodCollectionView {
button.accessibilityLabel = String.Localized.edit
return button
}()
lazy var defaultBadge: UILabel = {
let label = UILabel()
label.font = appearance.scaledFont(for: appearance.font.base.medium, style: .caption1, maximumPointSize: 20)
label.textColor = appearance.colors.textSecondary
label.adjustsFontForContentSizeCategory = true
label.text = String.Localized.default_text
label.isHidden = true
return label
}()

fileprivate var viewModel: SavedPaymentOptionsViewController.Selection?

var isRemovingPaymentMethods: Bool = false {
didSet {
if needsVerticalPaddingForBadge {
joyceqin-stripe marked this conversation as resolved.
Show resolved Hide resolved
if isRemovingPaymentMethods {
activateDefaultBadgeConstraints()
defaultBadge.setHiddenIfNecessary(!showDefaultPMBadge)
}
else {
deactivateDefaultBadgeConstraints()
defaultBadge.setHiddenIfNecessary(true)
}
}
update()
}
}
Expand All @@ -110,14 +141,17 @@ extension SavedPaymentMethodCollectionView {

var cbcEligible: Bool = false
var allowsPaymentMethodRemoval: Bool = true
var allowsSetAsDefaultPM: Bool = false
var needsVerticalPaddingForBadge: Bool = false
var showDefaultPMBadge: Bool = false

/// Indicates whether the cell for a saved payment method should display the edit icon.
/// True if payment methods can be removed or edited (will update this to include allowing set as default)
/// True if payment methods can be removed or edited
var showEditIcon: Bool {
guard UpdatePaymentMethodViewModel.supportedPaymentMethods.contains(where: { viewModel?.savedPaymentMethod?.type == $0 }) else {
fatalError("Payment method does not match supported saved payment methods.")
}
return allowsPaymentMethodRemoval || (viewModel?.savedPaymentMethod?.isCoBrandedCard ?? false && cbcEligible)
return allowsSetAsDefaultPM || allowsPaymentMethodRemoval || (viewModel?.savedPaymentMethod?.isCoBrandedCard ?? false && cbcEligible)
}

// MARK: - UICollectionViewCell
Expand All @@ -142,7 +176,7 @@ extension SavedPaymentMethodCollectionView {
paymentMethodLogo.contentMode = .scaleAspectFit
accessoryButton.addTarget(self, action: #selector(didSelectAccessory), for: .touchUpInside)
let views = [
label, shadowRoundedRectangle, paymentMethodLogo, plus, selectedIcon, accessoryButton,
label, shadowRoundedRectangle, paymentMethodLogo, plus, selectedIcon, accessoryButton, defaultBadge
]
views.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -159,7 +193,7 @@ extension SavedPaymentMethodCollectionView {

label.topAnchor.constraint(
equalTo: shadowRoundedRectangle.bottomAnchor, constant: 4),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
labelBottomConstraint,
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 2),
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),

Expand Down Expand Up @@ -188,6 +222,7 @@ extension SavedPaymentMethodCollectionView {
equalTo: contentView.trailingAnchor, constant: 0),
accessoryButton.topAnchor.constraint(
equalTo: contentView.topAnchor, constant: 0),

])
}

Expand All @@ -208,15 +243,33 @@ extension SavedPaymentMethodCollectionView {
}
}

// MARK: - Internal Methods
private lazy var labelBottomConstraint: NSLayoutConstraint = {
return label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
}()
private lazy var labelHeightConstraint: NSLayoutConstraint = {
return label.heightAnchor.constraint(equalToConstant: 20)
}()
private lazy var defaultBadgeConstraints: [NSLayoutConstraint] = {
return [
defaultBadge.topAnchor.constraint(
equalTo: label.bottomAnchor, constant: 4),
defaultBadge.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
defaultBadge.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 2),
defaultBadge.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
]
}()

func setViewModel(_ viewModel: SavedPaymentOptionsViewController.Selection, cbcEligible: Bool, allowsPaymentMethodRemoval: Bool) {
// MARK: - Internal Methods
func setViewModel(_ viewModel: SavedPaymentOptionsViewController.Selection, cbcEligible: Bool, allowsPaymentMethodRemoval: Bool, allowsSetAsDefaultPM: Bool = false, needsVerticalPaddingForBadge: Bool = false, showDefaultPMBadge: Bool = false) {
paymentMethodLogo.isHidden = false
plus.isHidden = true
shadowRoundedRectangle.isHidden = false
self.viewModel = viewModel
self.cbcEligible = cbcEligible
self.allowsPaymentMethodRemoval = allowsPaymentMethodRemoval
self.allowsSetAsDefaultPM = allowsSetAsDefaultPM
self.needsVerticalPaddingForBadge = needsVerticalPaddingForBadge
self.showDefaultPMBadge = showDefaultPMBadge
update()
}

Expand Down Expand Up @@ -372,6 +425,17 @@ extension SavedPaymentMethodCollectionView {
}()
}
}

private func activateDefaultBadgeConstraints() {
NSLayoutConstraint.deactivate([labelBottomConstraint])
NSLayoutConstraint.activate([labelHeightConstraint] + defaultBadgeConstraints)
}

private func deactivateDefaultBadgeConstraints() {
NSLayoutConstraint.deactivate(defaultBadgeConstraints + [labelHeightConstraint])
NSLayoutConstraint.activate([labelBottomConstraint])
}

}

// A circle with an image in the middle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ class SavedPaymentOptionsViewController: UIViewController {
}
set {
collectionView.isRemovingPaymentMethods = newValue
collectionView.performBatchUpdates({
collectionView.reloadSections(IndexSet(integer: 0))
animateHeightChange{self.collectionView.updateLayout()}
})
UIView.transition(with: collectionView,
duration: 0.3,
options: .transitionCrossDissolve,
Expand All @@ -147,6 +151,11 @@ class SavedPaymentOptionsViewController: UIViewController {
}
}
}

var hasDefault: Bool {
return viewModels.contains(where: { isDefaultPaymentMethod(savedPaymentMethodId: $0.savedPaymentMethod?.stripeId) })
}

var bottomNoticeAttributedString: NSAttributedString? {
if case .saved(let paymentMethod, _) = selectedPaymentOption {
if paymentMethod.usBankAccount != nil {
Expand Down Expand Up @@ -267,7 +276,7 @@ class SavedPaymentOptionsViewController: UIViewController {

// MARK: - Views
private lazy var collectionView: SavedPaymentMethodCollectionView = {
let collectionView = SavedPaymentMethodCollectionView(appearance: appearance)
let collectionView = SavedPaymentMethodCollectionView(appearance: appearance, needsVerticalPaddingForBadge: hasDefault)
collectionView.delegate = self
collectionView.dataSource = self
return collectionView
Expand Down Expand Up @@ -439,6 +448,11 @@ class SavedPaymentOptionsViewController: UIViewController {
collectionView.reloadItems(at: [selectedIndexPath])
}

private func isDefaultPaymentMethod(savedPaymentMethodId: String?) -> Bool {
guard configuration.allowsSetAsDefaultPM, let savedPaymentMethodId, let defaultPaymentMethod = elementsSession.customer?.getDefaultPaymentMethod() else { return false }
return savedPaymentMethodId == defaultPaymentMethod.stripeId
}

// MARK: - Helpers

/// Creates the list of viewmodels to display in the "saved payment methods" carousel e.g. `["+ Add", "Apple Pay", "Link", "Visa 4242"]`
Expand Down Expand Up @@ -508,7 +522,7 @@ extension SavedPaymentOptionsViewController: UICollectionViewDataSource, UIColle
stpAssertionFailure()
return UICollectionViewCell()
}
cell.setViewModel(viewModel, cbcEligible: cbcEligible, allowsPaymentMethodRemoval: self.configuration.allowsRemovalOfPaymentMethods)
cell.setViewModel(viewModel, cbcEligible: cbcEligible, allowsPaymentMethodRemoval: self.configuration.allowsRemovalOfPaymentMethods, allowsSetAsDefaultPM: configuration.allowsSetAsDefaultPM, needsVerticalPaddingForBadge: hasDefault, showDefaultPMBadge: isDefaultPaymentMethod(savedPaymentMethodId: viewModel.savedPaymentMethod?.stripeId))
cell.delegate = self
cell.isRemovingPaymentMethods = self.collectionView.isRemovingPaymentMethods
cell.appearance = appearance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class SavedPaymentMethodRowButton: UIView {

rowButton.isSelected = isSelected
chevronButton.isHidden = !canUpdate && !canRemove
defaultBadge?.font = state == .selected ? selectedDefaultBadgeFont : unselectedDefaultBadgeFont
}
}

Expand All @@ -47,6 +48,16 @@ final class SavedPaymentMethodRowButton: UIView {
}
}

let showDefaultPMBadge: Bool

private lazy var selectedDefaultBadgeFont: UIFont = {
appearance.scaledFont(for: appearance.font.base.medium, style: .caption1, maximumPointSize: 20)
}()

private lazy var unselectedDefaultBadgeFont: UIFont = {
appearance.scaledFont(for: appearance.font.base.regular, style: .caption1, maximumPointSize: 20)
}()

private var isEditing: Bool {
switch state {
case .selected, .unselected:
Expand Down Expand Up @@ -89,16 +100,28 @@ final class SavedPaymentMethodRowButton: UIView {
return chevronButton
}()

private lazy var defaultBadge: UILabel? = {
guard showDefaultPMBadge else { return nil }
let defaultBadge = UILabel()
defaultBadge.font = selectedDefaultBadgeFont
defaultBadge.textColor = appearance.colors.textSecondary
defaultBadge.adjustsFontForContentSizeCategory = true
defaultBadge.text = String.Localized.default_text
return defaultBadge
}()

private lazy var rowButton: RowButton = {
let button: RowButton = .makeForSavedPaymentMethod(paymentMethod: paymentMethod, appearance: appearance, rightAccessoryView: chevronButton, didTap: handleRowButtonTapped)
let button: RowButton = .makeForSavedPaymentMethod(paymentMethod: paymentMethod, appearance: appearance, defaultBadge: defaultBadge, rightAccessoryView: chevronButton, didTap: handleRowButtonTapped)

return button
}()

init(paymentMethod: STPPaymentMethod,
appearance: PaymentSheet.Appearance) {
appearance: PaymentSheet.Appearance,
showDefaultPMBadge: Bool = false) {
self.paymentMethod = paymentMethod
self.appearance = appearance
self.showDefaultPMBadge = showDefaultPMBadge
super.init(frame: .zero)

addAndPinSubview(rowButton)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ class VerticalSavedPaymentMethodsViewController: UIViewController {
}

/// Indicates whether the chevron should be shown
/// True if any saved payment methods can be removed or edited (will update this to include allowing set as default)
/// True if any saved payment methods can be removed or edited
var canRemoveOrEdit: Bool {
let hasSupportedSavedPaymentMethods = paymentMethods.allSatisfy{ UpdatePaymentMethodViewModel.supportedPaymentMethods.contains($0.type) }
guard hasSupportedSavedPaymentMethods else {
fatalError("Saved payment methods contain unsupported payment methods.")
}
return canRemovePaymentMethods || canEditPaymentMethods
return configuration.allowsSetAsDefaultPM || canRemovePaymentMethods || canEditPaymentMethods
}

private var selectedPaymentMethod: STPPaymentMethod? {
Expand Down Expand Up @@ -174,10 +174,16 @@ class VerticalSavedPaymentMethodsViewController: UIViewController {
setInitialState(selectedPaymentMethod: selectedPaymentMethod)
}

private func isDefaultPaymentMethod(paymentMethodId: String) -> Bool {
guard configuration.allowsSetAsDefaultPM, let defaultPaymentMethod = elementsSession.customer?.getDefaultPaymentMethod() else { return false }
return configuration.allowsSetAsDefaultPM && paymentMethodId == defaultPaymentMethod.stripeId
}

private func buildPaymentMethodRows(paymentMethods: [STPPaymentMethod]) -> [SavedPaymentMethodRowButton] {
return paymentMethods.map { paymentMethod in
let button = SavedPaymentMethodRowButton(paymentMethod: paymentMethod,
appearance: configuration.appearance)
appearance: configuration.appearance,
showDefaultPMBadge: isDefaultPaymentMethod(paymentMethodId: paymentMethod.stripeId))
button.delegate = self
return button
}
Expand Down Expand Up @@ -363,7 +369,8 @@ extension VerticalSavedPaymentMethodsViewController: UpdatePaymentMethodViewCont
}

// Create the new button
let newButton = SavedPaymentMethodRowButton(paymentMethod: updatedPaymentMethod, appearance: configuration.appearance)
let newButton = SavedPaymentMethodRowButton(paymentMethod: updatedPaymentMethod, appearance: configuration.appearance, showDefaultPMBadge: isDefaultPaymentMethod(paymentMethodId: updatedPaymentMethod.stripeId))

newButton.delegate = self
newButton.previousSelectedState = oldButton.previousSelectedState
newButton.state = oldButton.state
Expand Down
Loading
Loading