From 643b8ab60bd1d2b1a876600f72813e1d23f833e7 Mon Sep 17 00:00:00 2001 From: John Woo <99628984+wooj-stripe@users.noreply.github.com> Date: Wed, 13 Apr 2022 13:21:34 -0700 Subject: [PATCH] Add alert prior to removing bank account (v2) (#995) --- Stripe/AddPaymentMethodViewController.swift | 12 +++++++- Stripe/CircularButton.swift | 2 +- ...kViewController-WalletViewController.swift | 2 +- .../en.lproj/Localizable.strings | 11 ------- Stripe/SavedPaymentMethodCollectionView.swift | 4 +-- .../SavedPaymentOptionsViewController.swift | 14 ++------- .../USBankAccountPaymentMethodElement.swift | 29 +++++++++++++++++-- .../en.lproj/Localizable.strings | 9 ++++++ .../Source/Elements/Element.swift | 10 +++++++ .../Source/Helpers/String+Localized.swift | 16 ++++++++++ 10 files changed, 78 insertions(+), 31 deletions(-) diff --git a/Stripe/AddPaymentMethodViewController.swift b/Stripe/AddPaymentMethodViewController.swift index 18b36d20643..40864d0b432 100644 --- a/Stripe/AddPaymentMethodViewController.swift +++ b/Stripe/AddPaymentMethodViewController.swift @@ -103,7 +103,11 @@ class AddPaymentMethodViewController: UIViewController { private lazy var usBankAccountFormElement: PaymentMethodElement = { // We are keeping usBankAccountInfo in memory to preserve state // if the user switches payment method types - return makeElement(for: selectedPaymentMethodType) + let paymentMethodElement = makeElement(for: selectedPaymentMethodType) + if let usBankAccountPaymentMethodElement = paymentMethodElement as? USBankAccountPaymentMethodElement { + usBankAccountPaymentMethodElement.presentingViewControllerDelegate = self + } + return paymentMethodElement }() private lazy var paymentMethodFormElement: PaymentMethodElement = { if selectedPaymentMethodType == .USBankAccount { @@ -321,3 +325,9 @@ extension AddPaymentMethodViewController: ElementDelegate { animateHeightChange() } } + +extension AddPaymentMethodViewController: PresentingViewControllerDelegate { + func presentViewController(viewController: UIViewController, completion: (() -> Void)?) { + self.present(viewController, animated: true, completion: completion) + } +} diff --git a/Stripe/CircularButton.swift b/Stripe/CircularButton.swift index fbce5da021a..c7b771978af 100644 --- a/Stripe/CircularButton.swift +++ b/Stripe/CircularButton.swift @@ -78,7 +78,7 @@ class CircularButton: UIControl { dark: UIColor(red: 43.0 / 255.0, green: 43.0 / 255.0, blue: 47.0 / 255.0, alpha: 1)) imageView.image = Image.icon_x.makeImage(template: true) imageView.tintColor = dangerColor - accessibilityLabel = STPLocalizedString("Remove", "Text for remove button") + accessibilityLabel = String.Localized.remove accessibilityIdentifier = "CircularButton.Remove" } imageView.translatesAutoresizingMaskIntoConstraints = false diff --git a/Stripe/PayWithLinkViewController-WalletViewController.swift b/Stripe/PayWithLinkViewController-WalletViewController.swift index e38c6c60e84..b7c8fe92b26 100644 --- a/Stripe/PayWithLinkViewController-WalletViewController.swift +++ b/Stripe/PayWithLinkViewController-WalletViewController.swift @@ -275,7 +275,7 @@ private extension PayWithLinkViewController.WalletViewController { )) alertController.addAction(UIAlertAction( - title: "Remove", // TODO(ramont): Localize + title: String.Localized.remove, style: .destructive, handler: { _ in self.paymentPicker.showLoader(at: index) diff --git a/Stripe/Resources/Localizations/en.lproj/Localizable.strings b/Stripe/Resources/Localizations/en.lproj/Localizable.strings index 51ed1a1c1f9..620caf4d934 100644 --- a/Stripe/Resources/Localizations/en.lproj/Localizable.strings +++ b/Stripe/Resources/Localizations/en.lproj/Localizable.strings @@ -257,20 +257,9 @@ /* Payment Method type brand name. */ "Przelewy24" = "Przelewy24"; -/* Accessibility label for a button that removes a saved payment method - Button title for confirmation alert to remove a saved payment method - Text for remove button */ -"Remove" = "Remove"; - /* Content for alert popup prompting to confirm removing a saved card. Remove {card brand} ending in {last 4} e.g. 'Remove VISA ending in 4242' */ "Remove %1$@ ending in %2$@" = "Remove %1$@ ending in %2$@"; -/* Title for confirmation alert to remove a saved bank account payment method */ -"Remove bank account" = "Remove bank account"; - -/* Content for alert popup prompting to confirm removing a saved bank account. e.g. 'Remove bank account ending in 4242' */ -"Remove bank account ending in %@" = "Remove bank account ending in %@"; - /* Title for confirmation alert to remove a card */ "Remove Card" = "Remove Card"; diff --git a/Stripe/SavedPaymentMethodCollectionView.swift b/Stripe/SavedPaymentMethodCollectionView.swift index 9e17099e272..5783de8ea05 100644 --- a/Stripe/SavedPaymentMethodCollectionView.swift +++ b/Stripe/SavedPaymentMethodCollectionView.swift @@ -87,9 +87,7 @@ extension SavedPaymentMethodCollectionView { dangerColor: appearance.colors.danger) button.backgroundColor = appearance.colors.danger button.isAccessibilityElement = true - button.accessibilityLabel = STPLocalizedString( - "Remove", - "Accessibility label for a button that removes a saved payment method") + button.accessibilityLabel = String.Localized.remove button.accessibilityIdentifier = "Remove" return button }() diff --git a/Stripe/SavedPaymentOptionsViewController.swift b/Stripe/SavedPaymentOptionsViewController.swift index 76de4e83e30..48a04910559 100644 --- a/Stripe/SavedPaymentOptionsViewController.swift +++ b/Stripe/SavedPaymentOptionsViewController.swift @@ -312,9 +312,7 @@ extension SavedPaymentOptionsViewController: PaymentOptionCellDelegate { } let viewModel = viewModels[indexPath.row] let alert = UIAlertAction( - title: STPLocalizedString( - "Remove", "Button title for confirmation alert to remove a saved payment method" - ), style: .destructive + title: String.Localized.remove, style: .destructive ) { (_) in self.viewModels.remove(at: indexPath.row) // the deletion needs to be in a performBatchUpdates so we make sure it is completed @@ -377,15 +375,9 @@ extension STPPaymentMethod { ) case .SEPADebit: let last4 = sepaDebit?.last4 ?? "" - let formattedMessage = STPLocalizedString( - "Remove bank account ending in %@", - "Content for alert popup prompting to confirm removing a saved bank account. e.g. 'Remove bank account ending in 4242'" - ) + let formattedMessage = String.Localized.removeBankAccountEndingIn return ( - title: STPLocalizedString( - "Remove bank account", - "Title for confirmation alert to remove a saved bank account payment method" - ), + title: String.Localized.removeBankAccount, message: String(format: formattedMessage, last4) ) default: diff --git a/Stripe/USBankAccountPaymentMethodElement.swift b/Stripe/USBankAccountPaymentMethodElement.swift index b52af17932a..cf469c5c99b 100644 --- a/Stripe/USBankAccountPaymentMethodElement.swift +++ b/Stripe/USBankAccountPaymentMethodElement.swift @@ -10,6 +10,8 @@ import UIKit @_spi(STP) import StripeCore final class USBankAccountPaymentMethodElement : Element { + var presentingViewControllerDelegate: PresentingViewControllerDelegate? = nil + var delegate: ElementDelegate? = nil var view: UIView { @@ -111,9 +113,30 @@ final class USBankAccountPaymentMethodElement : Element { extension USBankAccountPaymentMethodElement: BankAccountInfoViewDelegate { func didTapXIcon() { - self.bankInfoSectionElement.view.isHidden = true - self.linkedBank = nil - self.delegate?.didUpdate(element: self) + let completionClosure = { + self.bankInfoSectionElement.view.isHidden = true + self.linkedBank = nil + self.delegate?.didUpdate(element: self) + } + + guard let last4BankAccount = self.linkedBank?.last4, + let presentingDelegate = presentingViewControllerDelegate else { + completionClosure() + return + } + + let didTapAlert = UIAlertAction(title: String.Localized.remove, style: .destructive) { (_) in + completionClosure() + } + let didTapCancel = UIAlertAction(title: String.Localized.cancel, + style: .cancel, + handler: nil) + let alertController = UIAlertController(title: String.Localized.removeBankAccount, + message: String(format: String.Localized.removeBankAccountEndingIn, last4BankAccount), + preferredStyle: .alert) + alertController.addAction(didTapCancel) + alertController.addAction(didTapAlert) + presentingDelegate.presentViewController(viewController: alertController, completion: nil) } } diff --git a/StripeUICore/StripeUICore/Resources/Localizations/en.lproj/Localizable.strings b/StripeUICore/StripeUICore/Resources/Localizations/en.lproj/Localizable.strings index d9600d3cf70..a6ebda712a9 100644 --- a/StripeUICore/StripeUICore/Resources/Localizations/en.lproj/Localizable.strings +++ b/StripeUICore/StripeUICore/Resources/Localizations/en.lproj/Localizable.strings @@ -107,6 +107,15 @@ Short string for postal code (text used in non-US countries) */ Label of an address field */ "Province" = "Province"; +/* Button title for confirmation alert to remove a saved payment method */ +"Remove" = "Remove"; + +/* Title for confirmation alert to remove a saved bank account payment method */ +"Remove bank account" = "Remove bank account"; + +/* Content for alert popup prompting to confirm removing a saved bank account. e.g. 'Remove bank account ending in 4242' */ +"Remove bank account ending in %@" = "Remove bank account ending in %@"; + /* Caption for State field on address form (only countries that use state , like United States) Label of an address field */ "State" = "State"; diff --git a/StripeUICore/StripeUICore/Source/Elements/Element.swift b/StripeUICore/StripeUICore/Source/Elements/Element.swift index c31088f05ec..19f4c5049bf 100644 --- a/StripeUICore/StripeUICore/Source/Elements/Element.swift +++ b/StripeUICore/StripeUICore/Source/Elements/Element.swift @@ -76,6 +76,16 @@ public extension Element { func continueToNextField(element: Element) } +/** + An Element uses this delegate to present a view controller + */ +@_spi(STP) public protocol PresentingViewControllerDelegate: ElementDelegate { + /** + Elements will call this function to delegate presentation of a view controller + */ + func presentViewController(viewController: UIViewController, completion: (() -> Void)?) +} + extension Element { /// A poorly named convenience method that returns all Elements underneath this Element, including this Element. func getAllSubElements() -> [Element] { diff --git a/StripeUICore/StripeUICore/Source/Helpers/String+Localized.swift b/StripeUICore/StripeUICore/Source/Helpers/String+Localized.swift index 7a941b465d0..f209d4bf5be 100644 --- a/StripeUICore/StripeUICore/Source/Helpers/String+Localized.swift +++ b/StripeUICore/StripeUICore/Source/Helpers/String+Localized.swift @@ -217,6 +217,18 @@ import Foundation "Error string displayed to user when they have entered an incomplete BSB number.") } + static var removeBankAccountEndingIn: String { + STPLocalizedString( + "Remove bank account ending in %@", + "Content for alert popup prompting to confirm removing a saved bank account. e.g. 'Remove bank account ending in 4242'") + } + + static var removeBankAccount: String { + STPLocalizedString( + "Remove bank account", + "Title for confirmation alert to remove a saved bank account payment method") + } + // MARK: - Control strings static var cancel: String { STPLocalizedString("Cancel", "Button title to cancel action in an alert") @@ -229,4 +241,8 @@ import Foundation static var `continue`: String { STPLocalizedString("Continue", "Text for continue button") } + + static var remove: String { + STPLocalizedString("Remove", "Button title for confirmation alert to remove a saved payment method") + } }