Skip to content

Commit

Permalink
Merge pull request #1461 from stripe-ios/kg-success-V2
Browse files Browse the repository at this point in the history
Financial Connections: Success Pane Polish
  • Loading branch information
kgaidis-stripe authored Sep 27, 2022
2 parents 39cd6d9 + 068fd9a commit ffb87cb
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 267 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@
6AE2E5B328DCFBEF00623523 /* String+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE2E5B228DCFBEF00623523 /* String+Localized.swift */; };
6AE2E5B528DE132300623523 /* AccountPickerAccountLoadErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE2E5B428DE132300623523 /* AccountPickerAccountLoadErrorView.swift */; };
6AE2E5B728DE7F4400623523 /* AccountPickerNoAccountEligibleErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE2E5B628DE7F4400623523 /* AccountPickerNoAccountEligibleErrorView.swift */; };
6AE5171828AAE2C0006E8314 /* SuccessHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE5171728AAE2C0006E8314 /* SuccessHeaderView.swift */; };
6AE5171A28AAE46A006E8314 /* SuccessFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE5171928AAE46A006E8314 /* SuccessFooterView.swift */; };
6AE5171C28AAF099006E8314 /* SuccessBodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE5171B28AAF099006E8314 /* SuccessBodyView.swift */; };
6AE5171E28ABEF5A006E8314 /* SuccessAccountListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE5171D28ABEF5A006E8314 /* SuccessAccountListView.swift */; };
Expand Down Expand Up @@ -304,7 +303,6 @@
6AE2E5B228DCFBEF00623523 /* String+Localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localized.swift"; sourceTree = "<group>"; };
6AE2E5B428DE132300623523 /* AccountPickerAccountLoadErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountPickerAccountLoadErrorView.swift; sourceTree = "<group>"; };
6AE2E5B628DE7F4400623523 /* AccountPickerNoAccountEligibleErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountPickerNoAccountEligibleErrorView.swift; sourceTree = "<group>"; };
6AE5171728AAE2C0006E8314 /* SuccessHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessHeaderView.swift; sourceTree = "<group>"; };
6AE5171928AAE46A006E8314 /* SuccessFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessFooterView.swift; sourceTree = "<group>"; };
6AE5171B28AAF099006E8314 /* SuccessBodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessBodyView.swift; sourceTree = "<group>"; };
6AE5171D28ABEF5A006E8314 /* SuccessAccountListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessAccountListView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -638,7 +636,6 @@
isa = PBXGroup;
children = (
6A1E29AC28A71B6100F99E9D /* SuccessViewController.swift */,
6AE5171728AAE2C0006E8314 /* SuccessHeaderView.swift */,
6AE5171B28AAF099006E8314 /* SuccessBodyView.swift */,
6AE5171928AAE46A006E8314 /* SuccessFooterView.swift */,
6A1E29AE28A71C5D00F99E9D /* SuccessDataSource.swift */,
Expand Down Expand Up @@ -871,7 +868,6 @@
6AD448B728C25F1F002CABB0 /* ResetFlowViewController.swift in Sources */,
6A1E29AA28A3FF8400F99E9D /* CheckboxView.swift in Sources */,
3CAD99CA2851155400B163EB /* InstitutionDataSource.swift in Sources */,
6AE5171828AAE2C0006E8314 /* SuccessHeaderView.swift in Sources */,
3C5430CF27457C8600B1E488 /* FinancialConnectionsAccount.swift in Sources */,
6A542DB12887259600958ED1 /* FeaturedInstitutionGridCell.swift in Sources */,
6A1BA4AE2858D76800759697 /* ConsentFooterView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ struct FinancialConnectionsSessionManifest: Decodable {
case terminalError = "terminal_error"
}

enum AccountDisconnectionMethod: String, SafeEnumCodable, Equatable {
case dashboard = "dashboard"
case support = "support"
case email = "email"
case link = "link"
case unparsable
}

// MARK: - Properties

let accountholderIsLinkConsumer: Bool?
Expand All @@ -67,6 +75,8 @@ struct FinancialConnectionsSessionManifest: Decodable {
let permissions: [StripeAPI.FinancialConnectionsAccount.Permissions]
let singleAccount: Bool
let paymentMethodType: FinancialConnectionsPaymentMethodType?
let accountDisconnectionMethod: AccountDisconnectionMethod?
let isEndUserFacing: Bool?
}

struct FinancialConnectionsAuthorizationSession: Decodable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private func CreateDataAccessDisclosureView(
let stackView = UIStackView(
arrangedSubviews: [
MerchantDataAccessView(
isStripeDirect: false,
isStripeDirect: isStripeDirect,
businessName: businessName,
permissions: permissions
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,13 +458,8 @@ extension AuthFlowController: SuccessViewControllerDelegate {
didSelectAnotherBank()
}

func successViewController(
_ viewController: SuccessViewController,
didCompleteSession session: StripeAPI.FinancialConnectionsSession
) {
let result = FinancialConnectionsSheet.Result.completed(session: session)
self.result = result // TODO(kgaidis): this needs to be set for some reason because of FinancialConnectionsNavigationControllerDelegate. However, it gives the illusion that calling didFinish below is what the result would be
delegate?.authFlow(controller: self, didFinish: result)
func successViewControllerDidSelectDone(_ viewController: SuccessViewController) {
closeAuthFlow(showConfirmationAlert: false, error: nil)
}
}

Expand Down Expand Up @@ -514,7 +509,7 @@ extension AuthFlowController: ResetFlowViewControllerDelegate {
_ viewController: ResetFlowViewController,
didFailWithError error: Error
) {
result = .failed(error: error)
result = .failed(error: error) // TODO(kgaidis): clean this up
delegate?.authFlow(controller: self, didFinish: result)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class SuccesIconView: UIView {
if #available(iOS 13.0, *) {
let image = UIImage(systemName: "checkmark")?
.withRenderingMode(.alwaysOriginal)
.withTintColor(.textSuccess)
.withTintColor(.white)
iconImageView.image = image
} else {
assertionFailure()
Expand All @@ -25,7 +25,7 @@ final class SuccesIconView: UIView {

init() {
super.init(frame: .zero)
backgroundColor = UIColor.clear
backgroundColor = UIColor.textSuccess
addSubview(iconImageView)

translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -47,9 +47,6 @@ final class SuccesIconView: UIView {
y: bounds.midY
)

// Draw circle border
layer.borderColor = UIColor.textSuccess.cgColor
layer.borderWidth = 1.5
layer.cornerRadius = bounds.size.width / 2.0
}
}
Expand All @@ -73,14 +70,12 @@ private struct SuccesIconViewUIViewRepresentable: UIViewRepresentable {
struct SuccesIconView_Previews: PreviewProvider {
@available(iOS 13.0.0, *)
static var previews: some View {
if #available(iOS 14.0, *) {
VStack {
SuccesIconViewUIViewRepresentable()
.frame(width: 40, height: 40)
Spacer()
}
.padding()
VStack {
SuccesIconViewUIViewRepresentable()
.frame(width: 40, height: 40)
Spacer()
}
.padding()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ private func CreateAccountRowView(institution: FinancialConnectionsInstitution,
displayableAccountNumberLabel.font = .stripeFont(forTextStyle: .captionEmphasized)
displayableAccountNumberLabel.textColor = .textSecondary
displayableAccountNumberLabel.text = "••••\(displayableAccountNumbers)"
// compress `account.name` instead of account number if text is long
displayableAccountNumberLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
horizontalStackView.addArrangedSubview(displayableAccountNumberLabel)
}

Expand All @@ -86,17 +88,19 @@ private func CreateAccountRowView(institution: FinancialConnectionsInstitution,
private func CreateIconWithLabelView(instituion: FinancialConnectionsInstitution, text: String) -> UIView {
let institutionIconImageView = CreateInstitutionIconView()

let institutionLabel = UILabel()
institutionLabel.font = .stripeFont(forTextStyle: .captionEmphasized)
institutionLabel.textColor = .textPrimary
institutionLabel.text = text
institutionLabel.translatesAutoresizingMaskIntoConstraints = false
institutionLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
let label = UILabel()
label.font = .stripeFont(forTextStyle: .captionEmphasized)
label.textColor = .textPrimary
label.text = text
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
// compress `account.name` instead of account number if text is long
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

let horizontalStackView = UIStackView(
arrangedSubviews: [
institutionIconImageView,
institutionLabel,
label,
]
)
horizontalStackView.axis = .horizontal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,40 @@ final class SuccessBodyView: UIView {
init(
institution: FinancialConnectionsInstitution,
linkedAccounts: [FinancialConnectionsPartnerAccount],
manifest: FinancialConnectionsSessionManifest
isStripeDirect: Bool,
businessName: String?,
permissions: [StripeAPI.FinancialConnectionsAccount.Permissions],
accountDisconnectionMethod: FinancialConnectionsSessionManifest.AccountDisconnectionMethod?,
isEndUserFacing: Bool
) {
super.init(frame: .zero)
let verticalStackView = UIStackView(
arrangedSubviews: [
let verticalStackView = UIStackView()
verticalStackView.axis = .vertical
verticalStackView.spacing = 12

if linkedAccounts.count > 0 {
verticalStackView.addArrangedSubview(
CreateInformationBoxView(
accountsListView: SuccessAccountListView(
institution: institution,
linkedAccounts: linkedAccounts
),
dataDisclosureView: CreateDataAccessDisclosureView(
businessName: manifest.businessName
isStripeDirect: isStripeDirect,
businessName: businessName,
permissions: permissions
)
),
CreateDisconnectAccountLabel()
]
)
)
}
verticalStackView.addArrangedSubview(
CreateDisconnectAccountLabel(
isLinkingOneAccount: (linkedAccounts.count == 1),
accountDisconnectionMethod: accountDisconnectionMethod ?? .email,
isEndUserFacing: isEndUserFacing
)
)
verticalStackView.axis = .vertical
verticalStackView.spacing = 12

addAndPinSubview(verticalStackView)
}

Expand Down Expand Up @@ -68,34 +83,26 @@ private func CreateInformationBoxView(
}

@available(iOSApplicationExtension, unavailable)
private func CreateDataAccessDisclosureView(businessName: String?) -> UIView {
private func CreateDataAccessDisclosureView(
isStripeDirect: Bool,
businessName: String?,
permissions: [StripeAPI.FinancialConnectionsAccount.Permissions]
) -> UIView {
let separatorView = UIView()
separatorView.backgroundColor = .borderNeutral
separatorView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
separatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.nativeScale),
])

// TODO(kgaidis): make the 'Data accessible to X' bold and localize/make-it-reusable as this also appears in success screen. `DataAccessText`
let textFront: String
if let businessName = businessName {
textFront = "Data accessible to \(businessName):"
} else {
textFront = "Data accessible to this business:"
}
// Data accessible to this business:
let text = "\(textFront) Account ownership details, account details through Stripe. [Learn more](https://support.stripe.com/user/questions/what-data-does-stripe-access-from-my-linked-financial-account)"
let dataAccessLabel = ClickableLabel()
dataAccessLabel.setText(
text,
font: .stripeFont(forTextStyle: .captionTight),
linkFont: .stripeFont(forTextStyle: .captionTightEmphasized)
)

let verticalStackView = UIStackView(
arrangedSubviews: [
separatorView,
dataAccessLabel,
MerchantDataAccessView(
isStripeDirect: isStripeDirect,
businessName: businessName,
permissions: permissions
),
]
)
verticalStackView.axis = .vertical
Expand All @@ -104,12 +111,51 @@ private func CreateDataAccessDisclosureView(businessName: String?) -> UIView {
}

@available(iOSApplicationExtension, unavailable)
private func CreateDisconnectAccountLabel() -> UIView { // TODO(kgaidis): localize this string or fetch from backend
private func CreateDisconnectAccountLabel(
isLinkingOneAccount: Bool,
accountDisconnectionMethod: FinancialConnectionsSessionManifest.AccountDisconnectionMethod,
isEndUserFacing: Bool
) -> UIView {
let disconnectYourAccountLocalizedString: String = {
if isLinkingOneAccount {
return STPLocalizedString("disconnect your account", "One part of larger text 'You can disconnect your account at any time.' The text instructs the user that the bank accounts they linked to Stripe, can always be disconnected later. The 'disconnect your account' part is clickable and will show user a support website.")
} else {
return STPLocalizedString("disconnect your accounts", "One part of larger text 'You can disconnect your account at any time.' The text instructs the user that the bank accounts they linked to Stripe, can always be disconnected later. The 'disconnect your account' part is clickable and will show user a support website.")
}
}()
let fullLocalizedString = STPLocalizedString("You can %@ at any time.", "The text instructs the user that the bank accounts they linked to Stripe, can always be disconnected later. '%@' will be replaced by 'disconnect your account', to form a full string: 'You can disconnect your account at any time.'.")
let disconnectionUrlString = DisconnectionURLString(
accountDisconnectionMethod: accountDisconnectionMethod,
isEndUserFacing: isEndUserFacing
)

let disconnectAccountLabel = ClickableLabel()
disconnectAccountLabel.setText(
"You can [disconnect your account](https://support.stripe.com/user/how-do-i-disconnect-my-linked-financial-account) any time.",
String(format: fullLocalizedString, "[\(disconnectYourAccountLocalizedString)](\(disconnectionUrlString))"),
font: .stripeFont(forTextStyle: .captionTight),
linkFont: .stripeFont(forTextStyle: .captionTightEmphasized)
)
return disconnectAccountLabel
}

private func DisconnectionURLString(
accountDisconnectionMethod: FinancialConnectionsSessionManifest.AccountDisconnectionMethod,
isEndUserFacing: Bool
) -> String {
switch accountDisconnectionMethod {
case .support:
if isEndUserFacing {
return "https://support.stripe.com/user/how-do-i-disconnect-my-linked-financial-account"
} else {
return "https://support.stripe.com/how-to-disconnect-a-linked-financial-account"
}
case .dashboard:
return "https://dashboard.stripe.com/settings/linked-accounts"
case .link:
return "https://support.link.co/questions/connecting-your-bank-account#how-do-i-disconnect-my-connected-bank-account"
case .unparsable:
fallthrough
case .email:
return "https://support.stripe.com/contact"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ protocol SuccessDataSource: AnyObject {
var linkedAccounts: [FinancialConnectionsPartnerAccount] { get }
var institution: FinancialConnectionsInstitution { get }
var showLinkMoreAccountsButton: Bool { get }

func completeFinancialConnectionsSession() -> Future<StripeAPI.FinancialConnectionsSession>
}

final class SuccessDataSourceImplementation: SuccessDataSource {
Expand All @@ -42,8 +40,4 @@ final class SuccessDataSourceImplementation: SuccessDataSource {
self.apiClient = apiClient
self.clientSecret = clientSecret
}

func completeFinancialConnectionsSession() -> Future<StripeAPI.FinancialConnectionsSession> {
return apiClient.completeFinancialConnectionsSession(clientSecret: clientSecret)
}
}
Loading

0 comments on commit ffb87cb

Please sign in to comment.