diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift index fed8a89a918..de0198d4875 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Common/HostController.swift @@ -201,6 +201,7 @@ private extension HostController { clientSecret: clientSecret, apiClient: apiClient, manifest: manifest, + configuration: configuration, sessionFetcher: sessionFetcher, returnURL: returnURL, elementsSessionContext: elementsSessionContext, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerAccountLoadErrorView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerAccountLoadErrorView.swift index f1a16c3819e..73864890db5 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerAccountLoadErrorView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerAccountLoadErrorView.swift @@ -15,6 +15,7 @@ final class AccountPickerAccountLoadErrorView: UIView { init( institution: FinancialConnectionsInstitution, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectAnotherBank: @escaping () -> Void, didSelectTryAgain: (() -> Void)?, // if nil, don't show button didSelectEnterBankDetailsManually: (() -> Void)? // if nil, don't show button @@ -76,7 +77,8 @@ final class AccountPickerAccountLoadErrorView: UIView { institution.name ), subtitle: subtitle, - contentView: nil + contentView: nil, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: primaryButtonConfiguration, @@ -113,6 +115,7 @@ private struct AccountPickerAccountLoadErrorViewUIViewRepresentable: UIViewRepre logo: nil ), appearance: appearance, + configuration: .init(), didSelectAnotherBank: {}, didSelectTryAgain: didSelectTryAgain, didSelectEnterBankDetailsManually: didSelectEnterBankDetailsManually diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerDataSource.swift index 5a7daa407ec..df1c1a7f69f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerDataSource.swift @@ -19,6 +19,7 @@ protocol AccountPickerDataSource: AnyObject { var delegate: AccountPickerDataSourceDelegate? { get set } var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var accountPickerPane: FinancialConnectionsAccountPickerPane? { get } var authSession: FinancialConnectionsAuthSession { get } var institution: FinancialConnectionsInstitution { get } @@ -44,6 +45,7 @@ final class AccountPickerDataSourceImplementation: AccountPickerDataSource { let accountPickerPane: FinancialConnectionsAccountPickerPane? let authSession: FinancialConnectionsAuthSession let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let institution: FinancialConnectionsInstitution let analyticsClient: FinancialConnectionsAnalyticsClient let reduceManualEntryProminenceInErrors: Bool @@ -63,6 +65,7 @@ final class AccountPickerDataSourceImplementation: AccountPickerDataSource { accountPickerPane: FinancialConnectionsAccountPickerPane?, authSession: FinancialConnectionsAuthSession, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, institution: FinancialConnectionsInstitution, analyticsClient: FinancialConnectionsAnalyticsClient, reduceManualEntryProminenceInErrors: Bool, @@ -74,6 +77,7 @@ final class AccountPickerDataSourceImplementation: AccountPickerDataSource { self.accountPickerPane = accountPickerPane self.authSession = authSession self.manifest = manifest + self.configuration = configuration self.institution = institution self.analyticsClient = analyticsClient self.reduceManualEntryProminenceInErrors = reduceManualEntryProminenceInErrors diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerNoAccountEligibleErrorView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerNoAccountEligibleErrorView.swift index 6c7648e541f..de36c2acfb0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerNoAccountEligibleErrorView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerNoAccountEligibleErrorView.swift @@ -20,6 +20,7 @@ final class AccountPickerNoAccountEligibleErrorView: UIView { numberOfIneligibleAccounts: Int, paymentMethodType: FinancialConnectionsPaymentMethodType, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectAnotherBank: @escaping () -> Void ) { super.init(frame: .zero) @@ -127,7 +128,8 @@ final class AccountPickerNoAccountEligibleErrorView: UIView { } }(), subtitle: subtitleFirstSentence + " " + subtitleSecondSentence, - contentView: nil + contentView: nil, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( @@ -180,6 +182,7 @@ private struct AccountPickerNoAccountEligibleErrorViewUIViewRepresentable: UIVie numberOfIneligibleAccounts: numberOfIneligibleAccounts, paymentMethodType: paymentMethodType, appearance: .stripe, + configuration: .init(), didSelectAnotherBank: {} ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerViewController.swift index 974448c5774..b24a93a198f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/AccountPickerViewController.swift @@ -102,19 +102,22 @@ final class AccountPickerViewController: UIViewController { let dataAccessNoticeViewController = DataAccessNoticeViewController( dataAccessNotice: dataAccessNotice, appearance: self.dataSource.manifest.appearance, + configuration: self.dataSource.configuration, didSelectUrl: { [weak self] url in guard let self = self else { return } AuthFlowHelpers.handleURLInTextFromBackend( url: url, pane: .accountPicker, analyticsClient: self.dataSource.analyticsClient, + configuration: self.dataSource.configuration, handleURL: { _, _ in } ) } ) + self.dataSource.configuration.style.configure(dataAccessNoticeViewController) dataAccessNoticeViewController.present(on: self) } else { - SFSafariViewController.present(url: url) + SFSafariViewController.present(url: url, configuration: self.dataSource.configuration) } } ) @@ -141,7 +144,8 @@ final class AccountPickerViewController: UIViewController { private func pollAuthSessionAccounts() { let retreivingAccountsLoadingView = RetrieveAccountsLoadingView( - institutionIconUrl: dataSource.institution.icon?.default + institutionIconUrl: dataSource.institution.icon?.default, + configuration: dataSource.configuration ) view.addAndPinSubviewToSafeArea(retreivingAccountsLoadingView) @@ -265,6 +269,7 @@ final class AccountPickerViewController: UIViewController { numberOfIneligibleAccounts: numberOfIneligibleAccounts, paymentMethodType: self.dataSource.manifest.paymentMethodType ?? .usBankAccount, appearance: self.dataSource.manifest.appearance, + configuration: self.dataSource.configuration, didSelectAnotherBank: self.didSelectAnotherBank ) // the user will never enter this instance of `AccountPickerViewController` @@ -318,7 +323,8 @@ final class AccountPickerViewController: UIViewController { } }(), subtitle: nil, - contentView: accountPickerSelectionView + contentView: accountPickerSelectionView, + configuration: dataSource.configuration ), footerView: footerView ) @@ -352,6 +358,7 @@ final class AccountPickerViewController: UIViewController { let errorView = AccountPickerAccountLoadErrorView( institution: dataSource.institution, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectAnotherBank: didSelectAnotherBank, didSelectTryAgain: didSelectTryAgain, didSelectEnterBankDetailsManually: didSelectManualEntry diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/RetrieveAccountsLoadingView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/RetrieveAccountsLoadingView.swift index 39e1922aa4d..8777898b8e5 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/RetrieveAccountsLoadingView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AccountPicker/RetrieveAccountsLoadingView.swift @@ -10,7 +10,7 @@ import UIKit final class RetrieveAccountsLoadingView: UIView { - init(institutionIconUrl: String?) { + init(institutionIconUrl: String?, configuration: FinancialConnectionsSheet.Configuration) { super.init(frame: .zero) let paneLayoutView = PaneLayoutView( contentView: PaneLayoutView.createContentView( @@ -40,7 +40,8 @@ final class RetrieveAccountsLoadingView: UIView { verticalStackView.axis = .vertical verticalStackView.spacing = 16 return verticalStackView - }() + }(), + configuration: configuration ), footerView: nil ) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AccountNumberRetrievalErrorView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AccountNumberRetrievalErrorView.swift index 45046144f33..f22bcbc454f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AccountNumberRetrievalErrorView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AccountNumberRetrievalErrorView.swift @@ -16,6 +16,7 @@ final class AccountNumberRetrievalErrorView: UIView { init( institution: FinancialConnectionsInstitution, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectAnotherBank: @escaping () -> Void, didSelectEnterBankDetailsManually: (() -> Void)? // if nil, don't show button ) { @@ -45,7 +46,8 @@ final class AccountNumberRetrievalErrorView: UIView { ) } }(), - contentView: nil + contentView: nil, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( @@ -93,6 +95,7 @@ private struct AccountNumberRetrievalErrorViewUIViewRepresentable: UIViewReprese logo: nil ), appearance: appearance, + configuration: .init(), didSelectAnotherBank: {}, didSelectEnterBankDetailsManually: didSelectEnterBankDetailsManually ) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountDataSource.swift index d0aaef37a08..ab73652d0c5 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountDataSource.swift @@ -11,6 +11,7 @@ import Foundation protocol AttachLinkedPaymentAccountDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var institution: FinancialConnectionsInstitution { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } var authSessionId: String? { get } @@ -24,6 +25,7 @@ final class AttachLinkedPaymentAccountDataSourceImplementation: AttachLinkedPaym private let apiClient: any FinancialConnectionsAPI private let clientSecret: String let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let institution: FinancialConnectionsInstitution private let linkedAccountId: String let analyticsClient: FinancialConnectionsAnalyticsClient @@ -35,6 +37,7 @@ final class AttachLinkedPaymentAccountDataSourceImplementation: AttachLinkedPaym apiClient: any FinancialConnectionsAPI, clientSecret: String, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, institution: FinancialConnectionsInstitution, linkedAccountId: String, analyticsClient: FinancialConnectionsAnalyticsClient, @@ -45,6 +48,7 @@ final class AttachLinkedPaymentAccountDataSourceImplementation: AttachLinkedPaym self.apiClient = apiClient self.clientSecret = clientSecret self.manifest = manifest + self.configuration = configuration self.institution = institution self.linkedAccountId = linkedAccountId self.analyticsClient = analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountViewController.swift index f11b66e7dec..68db2818904 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/AttachLinkedPaymentAccount/AttachLinkedPaymentAccountViewController.swift @@ -128,6 +128,7 @@ final class AttachLinkedPaymentAccountViewController: UIViewController { let errorView = AccountNumberRetrievalErrorView( institution: self.dataSource.institution, appearance: self.dataSource.manifest.appearance, + configuration: self.dataSource.configuration, didSelectAnotherBank: self.didSelectAnotherBank, didSelectEnterBankDetailsManually: self.didSelectManualEntry ) @@ -144,6 +145,7 @@ final class AttachLinkedPaymentAccountViewController: UIViewController { let errorView = AccountPickerAccountLoadErrorView( institution: self.dataSource.institution, appearance: self.dataSource.manifest.appearance, + configuration: self.dataSource.configuration, didSelectAnotherBank: self.didSelectAnotherBank, didSelectTryAgain: self.didSelectTryAgain, didSelectEnterBankDetailsManually: self.didSelectManualEntry diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift index aea4626980b..9aa531625d6 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentDataSource.swift @@ -10,6 +10,7 @@ import Foundation protocol ConsentDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var consent: FinancialConnectionsConsent { get } var merchantLogo: [String]? { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -20,6 +21,7 @@ protocol ConsentDataSource: AnyObject { final class ConsentDataSourceImplementation: ConsentDataSource { let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let consent: FinancialConnectionsConsent let merchantLogo: [String]? private let apiClient: any FinancialConnectionsAPI @@ -28,6 +30,7 @@ final class ConsentDataSourceImplementation: ConsentDataSource { init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, consent: FinancialConnectionsConsent, merchantLogo: [String]?, apiClient: any FinancialConnectionsAPI, @@ -35,6 +38,7 @@ final class ConsentDataSourceImplementation: ConsentDataSource { analyticsClient: FinancialConnectionsAnalyticsClient ) { self.manifest = manifest + self.configuration = configuration self.consent = consent self.merchantLogo = merchantLogo self.apiClient = apiClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift index c146440e1e7..aac6ca3ece5 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Consent/ConsentViewController.swift @@ -167,6 +167,7 @@ class ConsentViewController: UIViewController { url: url, pane: .consent, analyticsClient: dataSource.analyticsClient, + configuration: dataSource.configuration, handleURL: { urlHost, nextPaneOrDrawerOnSecondaryCta in guard let urlHost, let address = StripeSchemeAddress(rawValue: urlHost) else { self.dataSource @@ -193,10 +194,12 @@ class ConsentViewController: UIViewController { let dataAccessNoticeViewController = DataAccessNoticeViewController( dataAccessNotice: dataAccessNotice, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectUrl: { [weak self] url in self?.didSelectURLInTextFromBackend(url) } ) + dataSource.configuration.style.configure(dataAccessNoticeViewController) dataAccessNoticeViewController.present(on: self) } case .legalDatailsNotice: @@ -204,10 +207,12 @@ class ConsentViewController: UIViewController { let legalDetailsNoticeViewController = LegalDetailsNoticeViewController( legalDetailsNotice: legalDetailsNoticeModel, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectUrl: { [weak self] url in self?.didSelectURLInTextFromBackend(url) } ) + dataSource.configuration.style.configure(legalDetailsNoticeViewController) legalDetailsNoticeViewController.present(on: self) case .linkAccountPicker: delegate?.consentViewController( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorDataSource.swift index f261d6cedd4..dbd8d8a6d08 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorDataSource.swift @@ -13,6 +13,7 @@ final class ErrorDataSource { let error: Error let referrerPane: FinancialConnectionsSessionManifest.NextPane let manifest: FinancialConnectionsSessionManifest + var configuration: FinancialConnectionsSheet.Configuration let reduceManualEntryProminenceInErrors: Bool let analyticsClient: FinancialConnectionsAnalyticsClient let institution: FinancialConnectionsInstitution? @@ -21,6 +22,7 @@ final class ErrorDataSource { error: Error, referrerPane: FinancialConnectionsSessionManifest.NextPane, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, reduceManualEntryProminenceInErrors: Bool, analyticsClient: FinancialConnectionsAnalyticsClient, institution: FinancialConnectionsInstitution? @@ -28,6 +30,7 @@ final class ErrorDataSource { self.error = error self.referrerPane = referrerPane self.manifest = manifest + self.configuration = configuration self.reduceManualEntryProminenceInErrors = reduceManualEntryProminenceInErrors self.analyticsClient = analyticsClient self.institution = institution diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorViewController.swift index d61dbaa5105..4ea8c273848 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Error/ErrorViewController.swift @@ -123,7 +123,8 @@ final class ErrorViewController: UIViewController { }() return beginningOfSubtitle + " " + endOfSubtitle }(), - contentView: nil + contentView: nil, + configuration: dataSource.configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: primaryButtonConfiguration, @@ -167,7 +168,8 @@ final class ErrorViewController: UIViewController { ) } }(), - contentView: nil + contentView: nil, + configuration: dataSource.configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: primaryButtonConfiguration, @@ -202,6 +204,7 @@ final class ErrorViewController: UIViewController { errorView = TerminalErrorView( allowManualEntry: dataSource.manifest.allowManualEntry, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectManualEntry: { [weak self] in guard let self else { return } self.delegate?.errorViewControllerDidSelectManualEntry(self) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionDataSource.swift index 0953c2616d2..a157df9ad5c 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionDataSource.swift @@ -11,6 +11,7 @@ import Foundation protocol InstitutionDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } var featuredInstitutions: [FinancialConnectionsInstitution] { get } @@ -24,6 +25,7 @@ class InstitutionAPIDataSource: InstitutionDataSource { // MARK: - Properties let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration private let apiClient: any FinancialConnectionsAPI private let clientSecret: String let analyticsClient: FinancialConnectionsAnalyticsClient @@ -33,11 +35,13 @@ class InstitutionAPIDataSource: InstitutionDataSource { init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, apiClient: any FinancialConnectionsAPI, clientSecret: String, analyticsClient: FinancialConnectionsAnalyticsClient ) { self.manifest = manifest + self.configuration = configuration self.apiClient = apiClient self.clientSecret = clientSecret self.analyticsClient = analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionNoResultsView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionNoResultsView.swift index 25c3c24016b..e02477f82c3 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionNoResultsView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionNoResultsView.swift @@ -15,6 +15,7 @@ final class InstitutionNoResultsView: UIView { init( appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectManuallyEnterDetails: (() -> Void)? ) { self.didSelectManuallyEnterDetails = didSelectManuallyEnterDetails @@ -77,7 +78,8 @@ final class InstitutionNoResultsView: UIView { STPLocalizedString( "Try searching another bank", "The subtitle of a notice that appears at the bottom of search results. It appears when a user is searching for their bank, but no results are returned." - ) + ), + action: AttributedTextView.linkSelectedAction(with: configuration) ) } verticalStackView.addArrangedSubview(subtitleLabel) @@ -103,6 +105,7 @@ private struct InstitutionNoResultsViewUIViewRepresentable: UIViewRepresentable func makeUIView(context: Context) -> InstitutionNoResultsView { InstitutionNoResultsView( appearance: .stripe, + configuration: .init(), didSelectManuallyEnterDetails: (showManualEntry ? {} : nil) ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPickerViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPickerViewController.swift index fae2a428c47..ca9c159c1b1 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPickerViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionPickerViewController.swift @@ -100,7 +100,8 @@ class InstitutionPickerViewController: UIViewController { frame: view.bounds, allowManualEntry: dataSource.manifest.allowManualEntry, institutionSearchDisabled: dataSource.manifest.institutionSearchDisabled, - appearance: dataSource.manifest.appearance + appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration ) institutionTableView.delegate = self return institutionTableView diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionTableView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionTableView.swift index a931da56af4..1f5dc1652e0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionTableView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/InstitutionPicker/InstitutionTableView.swift @@ -35,6 +35,7 @@ final class InstitutionTableView: UIView { private let allowManualEntry: Bool private let institutionSearchDisabled: Bool private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration let tableView: UITableView private let dataSource: UITableViewDiffableDataSource weak var delegate: InstitutionTableViewDelegate? @@ -104,11 +105,13 @@ final class InstitutionTableView: UIView { frame: CGRect, allowManualEntry: Bool, institutionSearchDisabled: Bool, - appearance: FinancialConnectionsAppearance + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration ) { self.allowManualEntry = allowManualEntry self.institutionSearchDisabled = institutionSearchDisabled self.appearance = appearance + self.configuration = configuration let cellIdentifier = "\(InstitutionTableViewCell.self)" tableView = UITableView(frame: frame) dataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, _, institution in @@ -234,6 +237,7 @@ final class InstitutionTableView: UIView { true, view: InstitutionNoResultsView( appearance: appearance, + configuration: configuration, didSelectManuallyEnterDetails: self.allowManualEntry ? { [weak self] in guard let self = self else { return } self.delegate?.institutionTableView( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerDataSource.swift index 230eba8cd65..de85c431378 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerDataSource.swift @@ -19,6 +19,7 @@ protocol LinkAccountPickerDataSource: AnyObject { var delegate: LinkAccountPickerDataSourceDelegate? { get set } var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var selectedAccounts: [FinancialConnectionsAccountTuple] { get } var nextPaneOnAddAccount: FinancialConnectionsSessionManifest.NextPane? { get set } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -36,6 +37,7 @@ protocol LinkAccountPickerDataSource: AnyObject { final class LinkAccountPickerDataSourceImplementation: LinkAccountPickerDataSource { let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration var nextPaneOnAddAccount: FinancialConnectionsSessionManifest.NextPane? let analyticsClient: FinancialConnectionsAnalyticsClient private let apiClient: any FinancialConnectionsAPI @@ -83,6 +85,7 @@ final class LinkAccountPickerDataSourceImplementation: LinkAccountPickerDataSour init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, apiClient: any FinancialConnectionsAPI, analyticsClient: FinancialConnectionsAnalyticsClient, clientSecret: String, @@ -90,6 +93,7 @@ final class LinkAccountPickerDataSourceImplementation: LinkAccountPickerDataSour dataAccessNotice: FinancialConnectionsDataAccessNotice? ) { self.manifest = manifest + self.configuration = configuration self.apiClient = apiClient self.analyticsClient = analyticsClient self.clientSecret = clientSecret diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerViewController.swift index fd4471c056f..f8aa3791a77 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkAccountPicker/LinkAccountPickerViewController.swift @@ -71,13 +71,15 @@ final class LinkAccountPickerViewController: UIViewController { "The title of a screen that allows users to select which bank accounts they want to use to pay for something." ) } - }() + }(), + configuration: dataSource.configuration ), // `createBodyView` adds extra padding // around the loading view PaneLayoutView.createBodyView( text: nil, - contentView: LinkAccountPickerLoadingView() + contentView: LinkAccountPickerLoadingView(), + configuration: dataSource.configuration ), ] ) @@ -185,13 +187,15 @@ final class LinkAccountPickerViewController: UIViewController { contentStackView.addArrangedSubview( PaneLayoutView.createHeaderView( iconView: nil, - title: networkingAccountPicker.title + title: networkingAccountPicker.title, + configuration: dataSource.configuration ) ) contentStackView.addArrangedSubview( PaneLayoutView.createBodyView( text: nil, - contentView: bodyView + contentView: bodyView, + configuration: dataSource.configuration ) ) @@ -216,11 +220,13 @@ final class LinkAccountPickerViewController: UIViewController { let dataAccessNoticeViewController = DataAccessNoticeViewController( dataAccessNotice: dataAccessNotice, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectUrl: { [weak self] url in guard let self = self else { return } self.didSelectURLInTextFromBackend(url) } ) + self.dataSource.configuration.style.configure(dataAccessNoticeViewController) dataAccessNoticeViewController.present(on: self) } } @@ -504,6 +510,7 @@ final class LinkAccountPickerViewController: UIViewController { let genericInfoViewController = GenericInfoViewController( genericInfoScreen: drawerOnSelection, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, panePresentationStyle: .sheet, iconView: { if let institutionIconUrl = partnerAccount.institution?.icon?.default { @@ -540,6 +547,7 @@ final class LinkAccountPickerViewController: UIViewController { }, willDismissSheet: willDismissSheet ) + dataSource.configuration.style.configure(genericInfoViewController) genericInfoViewController.present(on: self) } @@ -548,6 +556,7 @@ final class LinkAccountPickerViewController: UIViewController { url: url, pane: .linkAccountPicker, analyticsClient: self.dataSource.analyticsClient, + configuration: dataSource.configuration, handleURL: { _, _ in } ) } @@ -578,6 +587,7 @@ extension LinkAccountPickerViewController: LinkAccountPickerBodyViewDelegate { let accountSelectionDrawerViewController = GenericInfoViewController( genericInfoScreen: drawerOnSelection, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, panePresentationStyle: .sheet, didSelectPrimaryButton: { genericInfoViewController in genericInfoViewController.dismiss(animated: true) @@ -587,6 +597,7 @@ extension LinkAccountPickerViewController: LinkAccountPickerBodyViewDelegate { self.didSelectURLInTextFromBackend(url) } ) + dataSource.configuration.style.configure(accountSelectionDrawerViewController) accountSelectionDrawerViewController.present(on: self) } else { // we will (likely) be presenting a different drawer further down the function diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift index 6cdeab7221a..f0847970a26 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginDataSource.swift @@ -10,6 +10,7 @@ import Foundation protocol LinkLoginDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var elementsSessionContext: ElementsSessionContext? { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -33,6 +34,7 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { private static let deallocatedError = FinancialConnectionsSheetError.unknown(debugDescription: "data source deallocated") let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let elementsSessionContext: ElementsSessionContext? let analyticsClient: FinancialConnectionsAnalyticsClient @@ -42,6 +44,7 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, analyticsClient: FinancialConnectionsAnalyticsClient, clientSecret: String, returnURL: String?, @@ -49,6 +52,7 @@ final class LinkLoginDataSourceImplementation: LinkLoginDataSource { elementsSessionContext: ElementsSessionContext? ) { self.manifest = manifest + self.configuration = configuration self.analyticsClient = analyticsClient self.clientSecret = clientSecret self.returnURL = returnURL diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginViewController.swift index 70c6ebee95d..8dd38e04752 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/LinkLogin/LinkLoginViewController.swift @@ -48,7 +48,8 @@ final class LinkLoginViewController: UIViewController { private lazy var formView: LinkSignupFormView = { let formView = LinkSignupFormView( accountholderPhoneNumber: dataSource.manifest.accountholderPhoneNumber, - appearance: dataSource.manifest.appearance + appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration ) formView.delegate = self return formView @@ -100,7 +101,8 @@ final class LinkLoginViewController: UIViewController { iconView: nil, title: linkLoginPane.title, subtitle: linkLoginPane.body, - contentView: formView + contentView: formView, + configuration: dataSource.configuration ) let footerView = PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( @@ -255,6 +257,7 @@ final class LinkLoginViewController: UIViewController { url: url, pane: .linkLogin, analyticsClient: dataSource.analyticsClient, + configuration: dataSource.configuration, handleURL: { _, _ in /* Stripe scheme URLs are not expected. */ } ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryDataSource.swift index 2a8edac8702..60ebdd91108 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryDataSource.swift @@ -11,6 +11,7 @@ import Foundation protocol ManualEntryDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } func attachBankAccountToLinkAccountSession(routingNumber: String, accountNumber: String) -> Future< @@ -23,6 +24,7 @@ final class ManualEntryDataSourceImplementation: ManualEntryDataSource { private let apiClient: any FinancialConnectionsAPI private let clientSecret: String let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let analyticsClient: FinancialConnectionsAnalyticsClient private let consumerSessionClientSecret: String? @@ -30,12 +32,14 @@ final class ManualEntryDataSourceImplementation: ManualEntryDataSource { apiClient: any FinancialConnectionsAPI, clientSecret: String, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, analyticsClient: FinancialConnectionsAnalyticsClient, consumerSessionClientSecret: String? ) { self.apiClient = apiClient self.clientSecret = clientSecret self.manifest = manifest + self.configuration = configuration self.analyticsClient = analyticsClient self.consumerSessionClientSecret = consumerSessionClientSecret } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryErrorView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryErrorView.swift index 03ec72f6a2b..77990c46aa9 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryErrorView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryErrorView.swift @@ -11,7 +11,7 @@ import UIKit final class ManualEntryErrorView: UIView { - init(text: String) { + init(text: String, configuration: FinancialConnectionsSheet.Configuration) { super.init(frame: .zero) let errorLabel = AttributedTextView( font: .label(.small), @@ -20,7 +20,7 @@ final class ManualEntryErrorView: UIView { textColor: FinancialConnectionsAppearance.Colors.textCritical, linkColor: FinancialConnectionsAppearance.Colors.textCritical ) - errorLabel.setText(text) + errorLabel.setText(text, action: AttributedTextView.linkSelectedAction(with: configuration)) addAndPinSubview(errorLabel) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryFormView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryFormView.swift index d423dcb00d4..5fedd0956bd 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryFormView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryFormView.swift @@ -43,7 +43,8 @@ final class ManualEntryFormView: UIView { "The title of a user-input-field that appears when a user is manually entering their bank account information. It instructs user to type the routing number." ), showDoneToolbar: true, - appearance: appearance + appearance: appearance, + configuration: configuration ) routingNumberTextField.textField.keyboardType = .numberPad routingNumberTextField.delegate = self @@ -57,7 +58,8 @@ final class ManualEntryFormView: UIView { "The title of a user-input-field that appears when a user is manually entering their bank account information. It instructs user to type the account number." ), showDoneToolbar: true, - appearance: appearance + appearance: appearance, + configuration: configuration ) accountNumberTextField.textField.keyboardType = .numberPad accountNumberTextField.delegate = self @@ -71,7 +73,8 @@ final class ManualEntryFormView: UIView { "The title of a user-input-field that appears when a user is manually entering their bank account information. It instructs user to re-type the account number to confirm it." ), showDoneToolbar: true, - appearance: appearance + appearance: appearance, + configuration: configuration ) accountNumberConfirmationTextField.textField.keyboardType = .numberPad accountNumberConfirmationTextField.delegate = self @@ -80,6 +83,7 @@ final class ManualEntryFormView: UIView { }() private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration private var didEndEditingOnceRoutingNumberTextField = false private var didEndEditingOnceAccountNumberTextField = false private var didEndEditingOnceAccountNumberConfirmationTextField = false @@ -98,8 +102,9 @@ final class ManualEntryFormView: UIView { return (routingNumberTextField.text, accountNumberTextField.text) } - init(isTestMode: Bool, appearance: FinancialConnectionsAppearance) { + init(isTestMode: Bool, appearance: FinancialConnectionsAppearance, configuration: FinancialConnectionsSheet.Configuration) { self.appearance = appearance + self.configuration = configuration super.init(frame: .zero) let contentVerticalStackView = UIStackView() @@ -158,7 +163,7 @@ final class ManualEntryFormView: UIView { linkColor: FinancialConnectionsAppearance.Colors.textCritical, alignment: .center ) - errorLabel.setText(text) + errorLabel.setText(text, action: AttributedTextView.linkSelectedAction(with: configuration)) let paddingStackView = UIStackView( arrangedSubviews: [ errorLabel diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryViewController.swift index f8b5d2f4b68..b8d66c24546 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/ManualEntry/ManualEntryViewController.swift @@ -27,7 +27,8 @@ final class ManualEntryViewController: UIViewController { private lazy var manualEntryFormView: ManualEntryFormView = { let manualEntryFormView = ManualEntryFormView( isTestMode: dataSource.manifest.isTestMode, - appearance: dataSource.manifest.appearance + appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration ) manualEntryFormView.delegate = self return manualEntryFormView @@ -97,7 +98,8 @@ final class ManualEntryViewController: UIViewController { } } }(), - contentView: manualEntryFormView + contentView: manualEntryFormView, + configuration: dataSource.configuration ), footerView: footerView.footerView, keepFooterAboveKeyboard: true diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift index 2c7f60814ec..d027c3a1c2f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift @@ -113,10 +113,12 @@ class NativeFlowController { if showConfirmationAlert { let closeConfirmationViewController = CloseConfirmationViewController( appearance: dataManager.manifest.appearance, + configuration: dataManager.configuration, didSelectClose: { finishClosingAuthFlow() } ) + dataManager.configuration.style.configure(closeConfirmationViewController) closeConfirmationViewController.present(on: navigationController) } else { finishClosingAuthFlow() @@ -1360,6 +1362,7 @@ private func CreatePaneViewController( accountPickerPane: dataManager.accountPickerPane, authSession: authSession, manifest: dataManager.manifest, + configuration: dataManager.configuration, institution: institution, analyticsClient: dataManager.analyticsClient, reduceManualEntryProminenceInErrors: dataManager.reduceManualEntryProminenceInErrors, @@ -1381,6 +1384,7 @@ private func CreatePaneViewController( apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, manifest: dataManager.manifest, + configuration: dataManager.configuration, institution: institution, linkedAccountId: linkedAccountId, analyticsClient: dataManager.analyticsClient, @@ -1404,6 +1408,7 @@ private func CreatePaneViewController( if let consentPaneModel = dataManager.consentPaneModel { let consentDataSource = ConsentDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, consent: consentPaneModel, merchantLogo: dataManager.merchantLogo, apiClient: dataManager.apiClient, @@ -1420,6 +1425,7 @@ private func CreatePaneViewController( case .institutionPicker: let dataSource = InstitutionAPIDataSource( manifest: dataManager.manifest, + configuration: dataManager.configuration, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, analyticsClient: dataManager.analyticsClient @@ -1431,6 +1437,7 @@ private func CreatePaneViewController( if let consumerSession = dataManager.consumerSession { let linkAccountPickerDataSource = LinkAccountPickerDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, apiClient: dataManager.apiClient, analyticsClient: dataManager.analyticsClient, clientSecret: dataManager.clientSecret, @@ -1452,6 +1459,7 @@ private func CreatePaneViewController( case .linkLogin: let linkLoginDataSource = LinkLoginDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, analyticsClient: dataManager.analyticsClient, clientSecret: dataManager.clientSecret, returnURL: dataManager.returnURL, @@ -1471,6 +1479,7 @@ private func CreatePaneViewController( apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, manifest: dataManager.manifest, + configuration: dataManager.configuration, analyticsClient: dataManager.analyticsClient, consumerSessionClientSecret: dataManager.consumerSession?.clientSecret ) @@ -1480,6 +1489,7 @@ private func CreatePaneViewController( case .networkingLinkSignupPane: let networkingLinkSignupDataSource = NetworkingLinkSignupDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, selectedAccounts: dataManager.linkedAccounts, returnURL: dataManager.returnURL, apiClient: dataManager.apiClient, @@ -1499,6 +1509,7 @@ private func CreatePaneViewController( let networkingLinkVerificationDataSource = NetworkingLinkVerificationDataSourceImplementation( accountholderCustomerEmailAddress: accountholderCustomerEmailAddress, manifest: dataManager.manifest, + configuration: dataManager.configuration, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, returnURL: dataManager.returnURL, @@ -1517,6 +1528,7 @@ private func CreatePaneViewController( { let networkingSaveToLinkVerificationDataSource = NetworkingSaveToLinkVerificationDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, consumerSession: consumerSession, selectedAccounts: dataManager.linkedAccounts, apiClient: dataManager.apiClient, @@ -1541,6 +1553,7 @@ private func CreatePaneViewController( consumerSession: consumerSession, selectedAccountIds: selectedAccountIds, manifest: dataManager.manifest, + configuration: dataManager.configuration, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, analyticsClient: dataManager.analyticsClient @@ -1560,6 +1573,7 @@ private func CreatePaneViewController( authSession: dataManager.authSession, institution: institution, manifest: dataManager.manifest, + configuration: dataManager.configuration, returnURL: dataManager.returnURL, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, @@ -1580,6 +1594,7 @@ private func CreatePaneViewController( case .success: let successDataSource = SuccessDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, linkedAccountsCount: dataManager.linkedAccounts?.count ?? 0, saveToLinkWithStripeSucceeded: dataManager.saveToLinkWithStripeSucceeded, apiClient: dataManager.apiClient, @@ -1600,6 +1615,7 @@ private func CreatePaneViewController( error: errorPaneError, referrerPane: errorPaneReferrerPane, manifest: dataManager.manifest, + configuration: dataManager.configuration, reduceManualEntryProminenceInErrors: dataManager.reduceManualEntryProminenceInErrors, analyticsClient: dataManager.analyticsClient, institution: dataManager.institution @@ -1618,6 +1634,7 @@ private func CreatePaneViewController( case .networkingLinkLoginWarmup: let networkingLinkWarmupDataSource = NetworkingLinkLoginWarmupDataSourceImplementation( manifest: dataManager.manifest, + configuration: dataManager.configuration, apiClient: dataManager.apiClient, clientSecret: dataManager.clientSecret, analyticsClient: dataManager.analyticsClient, @@ -1648,7 +1665,8 @@ private func CreatePaneViewController( let terminalErrorViewController = TerminalErrorViewController( error: terminalError, allowManualEntry: dataManager.manifest.allowManualEntry, - appearance: dataManager.manifest.appearance + appearance: dataManager.manifest.appearance, + configuration: dataManager.configuration ) terminalErrorViewController.delegate = nativeFlowController viewController = terminalErrorViewController diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupDataSource.swift index 2366910f170..2a9c7b8f0f0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupDataSource.swift @@ -10,6 +10,7 @@ import Foundation protocol NetworkingLinkLoginWarmupDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } func disableNetworking() -> Future @@ -18,6 +19,7 @@ protocol NetworkingLinkLoginWarmupDataSource: AnyObject { final class NetworkingLinkLoginWarmupDataSourceImplementation: NetworkingLinkLoginWarmupDataSource { let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration private let apiClient: any FinancialConnectionsAPI private let clientSecret: String let analyticsClient: FinancialConnectionsAnalyticsClient @@ -25,12 +27,14 @@ final class NetworkingLinkLoginWarmupDataSourceImplementation: NetworkingLinkLog init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, apiClient: any FinancialConnectionsAPI, clientSecret: String, analyticsClient: FinancialConnectionsAnalyticsClient, nextPaneOrDrawerOnSecondaryCta: String? ) { self.manifest = manifest + self.configuration = configuration self.apiClient = apiClient self.clientSecret = clientSecret self.analyticsClient = analyticsClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupViewController.swift index 03ce98823d1..cd868622633 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkLoginWarmup/NetworkingLinkLoginWarmupViewController.swift @@ -74,7 +74,8 @@ final class NetworkingLinkLoginWarmupViewController: SheetViewController { // since the email is only used as a visual, it's not worth to throw an error // if it is null email: dataSource.manifest.accountholderCustomerEmailAddress ?? "you" - ) + ), + configuration: dataSource.configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/EmailTextField.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/EmailTextField.swift index d345047187b..b2aa719a699 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/EmailTextField.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/EmailTextField.swift @@ -21,6 +21,7 @@ protocol EmailTextFieldDelegate: AnyObject { final class EmailTextField: UIView { private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration fileprivate lazy var textField: RoundedTextField = { let textField = RoundedTextField( placeholder: STPLocalizedString( @@ -28,7 +29,8 @@ final class EmailTextField: UIView { "The title of a user-input-field that appears when a user is signing up to Link (a payment service). It instructs user to type an email address." ), showDoneToolbar: true, - appearance: appearance + appearance: appearance, + configuration: configuration ) textField.textField.keyboardType = .emailAddress textField.textField.textContentType = .emailAddress @@ -63,8 +65,9 @@ final class EmailTextField: UIView { weak var delegate: EmailTextFieldDelegate? - init(appearance: FinancialConnectionsAppearance) { + init(appearance: FinancialConnectionsAppearance, configuration: FinancialConnectionsSheet.Configuration) { self.appearance = appearance + self.configuration = configuration super.init(frame: .zero) addAndPinSubview(textField) } @@ -148,9 +151,10 @@ private struct EmailTextFieldUIViewRepresentable: UIViewRepresentable { let text: String let isLoading: Bool let appearance: FinancialConnectionsAppearance + let configuration: FinancialConnectionsSheet.Configuration func makeUIView(context: Context) -> EmailTextField { - EmailTextField(appearance: appearance) + EmailTextField(appearance: appearance, configuration: configuration) } func updateUIView( @@ -174,37 +178,43 @@ struct EmailTextField_Previews: PreviewProvider { EmailTextFieldUIViewRepresentable( text: "", isLoading: false, - appearance: .stripe + appearance: .stripe, + configuration: .init() ).frame(height: 56) EmailTextFieldUIViewRepresentable( text: "test@test.com", isLoading: false, - appearance: .stripe + appearance: .stripe, + configuration: .init() ).frame(height: 56) EmailTextFieldUIViewRepresentable( text: "test@test-very-long-name-thats-very-long.com", isLoading: true, - appearance: .stripe + appearance: .stripe, + configuration: .init() ).frame(height: 56) EmailTextFieldUIViewRepresentable( text: "wrongemail@wronger", isLoading: false, - appearance: .stripe + appearance: .stripe, + configuration: .init() ).frame(height: 90) EmailTextFieldUIViewRepresentable( text: "light@theme.com", isLoading: true, - appearance: .stripe + appearance: .stripe, + configuration: .init() ).frame(height: 56) EmailTextFieldUIViewRepresentable( text: "linklight@theme.com", isLoading: true, - appearance: .link + appearance: .link, + configuration: .init() ).frame(height: 56) Spacer() diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/LinkSignupFormView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/LinkSignupFormView.swift index 46f83b332c8..dca7f0e3b46 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/LinkSignupFormView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/LinkSignupFormView.swift @@ -24,6 +24,7 @@ final class LinkSignupFormView: UIView { private let accountholderPhoneNumber: String? private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration weak var delegate: LinkSignupFormViewDelegate? private lazy var verticalStackView: UIStackView = { @@ -35,21 +36,33 @@ final class LinkSignupFormView: UIView { return verticalStackView }() private(set) lazy var emailTextField: EmailTextField = { - let emailTextField = EmailTextField(appearance: appearance) + let emailTextField = EmailTextField( + appearance: appearance, + configuration: configuration + ) emailTextField.delegate = self return emailTextField }() private(set) lazy var phoneTextField: PhoneTextField = { - let phoneTextField = PhoneTextField(defaultPhoneNumber: accountholderPhoneNumber, appearance: appearance) + let phoneTextField = PhoneTextField( + defaultPhoneNumber: accountholderPhoneNumber, + appearance: appearance, + configuration: configuration + ) phoneTextField.delegate = self return phoneTextField }() private var debounceEmailTimer: Timer? private var lastValidEmail: String? - init(accountholderPhoneNumber: String?, appearance: FinancialConnectionsAppearance) { - self.appearance = appearance + init( + accountholderPhoneNumber: String?, + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration + ) { self.accountholderPhoneNumber = accountholderPhoneNumber + self.appearance = appearance + self.configuration = configuration super.init(frame: .zero) addAndPinSubview(verticalStackView) phoneTextField.isHidden = true @@ -205,7 +218,7 @@ import SwiftUI private struct NetworkingLinkSignupBodyFormViewUIViewRepresentable: UIViewRepresentable { func makeUIView(context: Context) -> LinkSignupFormView { - LinkSignupFormView(accountholderPhoneNumber: nil, appearance: .stripe) + LinkSignupFormView(accountholderPhoneNumber: nil, appearance: .stripe, configuration: .init()) } func updateUIView(_ uiView: LinkSignupFormView, context: Context) {} diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift index 0399ddb28cb..f65749738f2 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupDataSource.swift @@ -10,6 +10,7 @@ import Foundation protocol NetworkingLinkSignupDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var elementsSessionContext: ElementsSessionContext? { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -29,6 +30,7 @@ protocol NetworkingLinkSignupDataSource: AnyObject { final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDataSource { let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let elementsSessionContext: ElementsSessionContext? private let selectedAccounts: [FinancialConnectionsPartnerAccount]? private let returnURL: String? @@ -38,6 +40,7 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, selectedAccounts: [FinancialConnectionsPartnerAccount]?, returnURL: String?, apiClient: any FinancialConnectionsAPI, @@ -46,6 +49,7 @@ final class NetworkingLinkSignupDataSourceImplementation: NetworkingLinkSignupDa elementsSessionContext: ElementsSessionContext? ) { self.manifest = manifest + self.configuration = configuration self.selectedAccounts = selectedAccounts self.returnURL = returnURL self.apiClient = apiClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupViewController.swift index 4329ea8c885..7af6521bdab 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/NetworkingLinkSignupViewController.swift @@ -43,7 +43,8 @@ final class NetworkingLinkSignupViewController: UIViewController { private lazy var formView: LinkSignupFormView = { let formView = LinkSignupFormView( accountholderPhoneNumber: dataSource.manifest.accountholderPhoneNumber, - appearance: dataSource.manifest.appearance + appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration ) formView.delegate = self return formView @@ -162,7 +163,8 @@ final class NetworkingLinkSignupViewController: UIViewController { legalDetailsNotice: networkingLinkSignup.legalDetailsNotice ) } - ) + ), + configuration: dataSource.configuration ), footerView: footerView ) @@ -269,11 +271,14 @@ final class NetworkingLinkSignupViewController: UIViewController { url: url, pane: .networkingLinkSignupPane, analyticsClient: dataSource.analyticsClient, - handleURL: { urlHost, _ in + configuration: dataSource.configuration, + handleURL: { [weak self] urlHost, _ in + guard let self else { return } if urlHost == "legal-details-notice", let legalDetailsNotice { let legalDetailsNoticeViewController = LegalDetailsNoticeViewController( legalDetailsNotice: legalDetailsNotice, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectUrl: { [weak self] url in self?.didSelectURLInTextFromBackend( url, @@ -281,6 +286,7 @@ final class NetworkingLinkSignupViewController: UIViewController { ) } ) + self.dataSource.configuration.style.configure(legalDetailsNoticeViewController) legalDetailsNoticeViewController.present(on: self) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/PhoneTextField.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/PhoneTextField.swift index 259afaa156b..e6f677a705d 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/PhoneTextField.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkSignupPane/PhoneTextField.swift @@ -23,7 +23,8 @@ final class PhoneTextField: UIView { let textField = RoundedTextField( placeholder: STPLocalizedString("Phone number", "The title of a user-input-field that appears when a user is signing up to Link (a payment service). It instructs user to type a phone number."), showDoneToolbar: true, - appearance: appearance + appearance: appearance, + configuration: configuration ) textField.textField.keyboardType = .phonePad textField.textField.textContentType = .telephoneNumber @@ -45,6 +46,7 @@ final class PhoneTextField: UIView { }() private let countryCodeSelectorView: PhoneCountryCodeSelectorView private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration // we will only start validating as user // types once editing ends fileprivate var didEndEditingOnce = false @@ -76,7 +78,11 @@ final class PhoneTextField: UIView { weak var delegate: PhoneTextFieldDelegate? - init(defaultPhoneNumber: String?, appearance: FinancialConnectionsAppearance) { + init( + defaultPhoneNumber: String?, + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration + ) { var defaultPhoneNumber = defaultPhoneNumber var defaultCountryCode: String? if let _defaultPhoneNumber = defaultPhoneNumber, let e164PhoneNumber = PhoneNumber.fromE164(_defaultPhoneNumber) { @@ -88,6 +94,7 @@ final class PhoneTextField: UIView { appearance: appearance ) self.appearance = appearance + self.configuration = configuration super.init(frame: .zero) countryCodeSelectorView.delegate = self addAndPinSubview(textField) @@ -219,7 +226,7 @@ private struct PhoneTextFieldUIViewRepresentable: UIViewRepresentable { let defaultPhoneNumber: String func makeUIView(context: Context) -> PhoneTextField { - PhoneTextField(defaultPhoneNumber: defaultPhoneNumber, appearance: .stripe) + PhoneTextField(defaultPhoneNumber: defaultPhoneNumber, appearance: .stripe, configuration: .init()) } func updateUIView( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationDataSource.swift index 18461389fff..8f2f945fde6 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationDataSource.swift @@ -32,6 +32,7 @@ final class NetworkingLinkStepUpVerificationDataSourceImplementation: Networking consumerSession: ConsumerSessionData, selectedAccountIds: [String], manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, apiClient: any FinancialConnectionsAPI, clientSecret: String, analyticsClient: FinancialConnectionsAnalyticsClient @@ -45,6 +46,7 @@ final class NetworkingLinkStepUpVerificationDataSourceImplementation: Networking let networkingOTPDataSource = NetworkingOTPDataSourceImplementation( otpType: "EMAIL", manifest: manifest, + configuration: configuration, emailAddress: consumerSession.emailAddress, customEmailType: "NETWORKED_CONNECTIONS_OTP_EMAIL", connectionsMerchantName: manifest.businessName, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationViewController.swift index 72889b07c89..075409fed17 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkStepUpVerification/NetworkingLinkStepUpVerificationViewController.swift @@ -112,7 +112,8 @@ final class NetworkingLinkStepUpVerificationViewController: UIViewController { "The subtitle/description of a screen where users are asked to enter a one-time-password (OTP) that they received in their email. '%@' is replaced with an email, for example, 'test@test.com'." ), dataSource.consumerSession.emailAddress ), - contentView: bodyView + contentView: bodyView, + configuration: dataSource.networkingOTPDataSource.configuration ), footerView: nil ) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift index e635cd1f142..82543519bfa 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationDataSource.swift @@ -39,6 +39,7 @@ final class NetworkingLinkVerificationDataSourceImplementation: NetworkingLinkVe init( accountholderCustomerEmailAddress: String, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, apiClient: any FinancialConnectionsAPI, clientSecret: String, returnURL: String?, @@ -53,6 +54,7 @@ final class NetworkingLinkVerificationDataSourceImplementation: NetworkingLinkVe let networkingOTPDataSource = NetworkingOTPDataSourceImplementation( otpType: "SMS", manifest: manifest, + configuration: configuration, emailAddress: accountholderCustomerEmailAddress, customEmailType: nil, connectionsMerchantName: nil, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationViewController.swift index 89f7eef2edb..a131c7ba29f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingLinkVerification/NetworkingLinkVerificationViewController.swift @@ -72,7 +72,8 @@ final class NetworkingLinkVerificationViewController: UIViewController { "Enter the code sent to %@", "The subtitle/description of a screen where users are informed that they have received a One-Type-Password (OTP) to their phone. '%@' gets replaced by a redacted phone number." ), AuthFlowHelpers.formatRedactedPhoneNumber(redactedPhoneNumber)), - contentView: otpView + contentView: otpView, + configuration: dataSource.networkingOTPDataSource.configuration ), footerView: nil ) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationDataSource.swift index 1448a6b2d22..48df848bfbf 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationDataSource.swift @@ -30,6 +30,7 @@ final class NetworkingSaveToLinkVerificationDataSourceImplementation: Networking init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, consumerSession: ConsumerSessionData, selectedAccounts: [FinancialConnectionsPartnerAccount]?, apiClient: any FinancialConnectionsAPI, @@ -45,6 +46,7 @@ final class NetworkingSaveToLinkVerificationDataSourceImplementation: Networking let networkingOTPDataSource = NetworkingOTPDataSourceImplementation( otpType: "SMS", manifest: manifest, + configuration: configuration, emailAddress: consumerSession.emailAddress, customEmailType: nil, connectionsMerchantName: nil, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationViewController.swift index f519dc4d4a5..e7ca883adc0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NetworkingSaveToLinkVerification/NetworkingSaveToLinkVerificationViewController.swift @@ -76,7 +76,8 @@ final class NetworkingSaveToLinkVerificationViewController: UIViewController { "Enter the code sent to %@.", "The subtitle/description of a screen where users are informed that they have received a One-Type-Password (OTP) to their phone. '%@' gets replaced by a redacted phone number." ), AuthFlowHelpers.formatRedactedPhoneNumber(redactedPhoneNumber)), - contentView: otpView + contentView: otpView, + configuration: dataSource.networkingOTPDataSource.configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: nil, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift index 39d848cad1c..6975e421f96 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthDataSource.swift @@ -11,6 +11,7 @@ import Foundation protocol PartnerAuthDataSource: AnyObject { var institution: FinancialConnectionsInstitution { get } var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var returnURL: String? { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } var pendingAuthSession: FinancialConnectionsAuthSession? { get } @@ -28,6 +29,7 @@ final class PartnerAuthDataSourceImplementation: PartnerAuthDataSource { let institution: FinancialConnectionsInstitution let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let returnURL: String? private let apiClient: any FinancialConnectionsAPI private let clientSecret: String @@ -47,6 +49,7 @@ final class PartnerAuthDataSourceImplementation: PartnerAuthDataSource { authSession: FinancialConnectionsAuthSession?, institution: FinancialConnectionsInstitution, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, returnURL: String?, apiClient: any FinancialConnectionsAPI, clientSecret: String, @@ -55,6 +58,7 @@ final class PartnerAuthDataSourceImplementation: PartnerAuthDataSource { self.pendingAuthSession = authSession self.institution = institution self.manifest = manifest + self.configuration = configuration self.returnURL = returnURL self.apiClient = apiClient self.clientSecret = clientSecret diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthViewController.swift index 4b5c6c03cc9..243fe0cc89a 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PartnerAuthViewController.swift @@ -136,6 +136,7 @@ final class PartnerAuthViewController: SheetViewController { isRepairSession: false, // TODO(kgaidis): change this for repair sessions panePresentationStyle: panePresentationStyle, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectURL: { [weak self] url in self?.didSelectURLInTextFromBackend(url) }, @@ -291,6 +292,7 @@ final class PartnerAuthViewController: SheetViewController { let continueStateViews = ContinueStateViews( institutionImageUrl: institution.icon?.default, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectContinue: { [weak self] in guard let self else { return } self.dataSource.analyticsClient.log( @@ -547,16 +549,20 @@ final class PartnerAuthViewController: SheetViewController { url: url, pane: .partnerAuth, analyticsClient: dataSource.analyticsClient, - handleURL: { urlHost, _ in + configuration: dataSource.configuration, + handleURL: { [weak self] urlHost, _ in + guard let self else { return } if urlHost == "data-access-notice" { if let dataAccessNoticeModel = dataSource.pendingAuthSession?.display?.text?.oauthPrepane?.dataAccessNotice { let dataAccessNoticeViewController = DataAccessNoticeViewController( dataAccessNotice: dataAccessNoticeModel, appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration, didSelectUrl: { [weak self] url in self?.didSelectURLInTextFromBackend(url) } ) + self.dataSource.configuration.style.configure(dataAccessNoticeViewController) dataAccessNoticeViewController.present(on: self) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PrepaneViews.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PrepaneViews.swift index eedaa6e086f..71eebb19c22 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PrepaneViews.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/PartnerAuth/PrepaneViews.swift @@ -35,6 +35,7 @@ final class PrepaneViews { isRepairSession: Bool, panePresentationStyle: PanePresentationStyle, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectURL: @escaping (URL) -> Void, didSelectContinue: @escaping () -> Void, didSelectCancel: @escaping () -> Void @@ -52,14 +53,16 @@ final class PrepaneViews { } }(), title: prepaneModel.title, - isSheet: (panePresentationStyle == .sheet) + isSheet: (panePresentationStyle == .sheet), + configuration: configuration ) self.bodyView = PaneLayoutView.createBodyView( text: prepaneModel.subtitle, contentView: CreateContentView( prepaneBodyModel: prepaneModel.body, didSelectURL: didSelectURL - ) + ), + configuration: configuration ) contentStackView.addArrangedSubview(headerView) @@ -210,6 +213,7 @@ private class PrepanePreviewView: UIView { isRepairSession: false, panePresentationStyle: .sheet, appearance: .stripe, + configuration: .init(), didSelectURL: { _ in }, didSelectContinue: {}, didSelectCancel: {} diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AttributedTextView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AttributedTextView.swift index ac0ab676dd6..24780461321 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AttributedTextView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AttributedTextView.swift @@ -98,12 +98,16 @@ final class AttributedTextView: HitTestView { fatalError("init(coder:) has not been implemented") } + static func linkSelectedAction(with configuration: FinancialConnectionsSheet.Configuration) -> ((URL) -> Void) { + return { url in + SFSafariViewController.present(url: url, configuration: configuration) + } + } + /// Helper that automatically handles extracting links and, optionally, opening it via `SFSafariViewController` - func setText( + func setText( _ text: String, - action: @escaping ((URL) -> Void) = { url in - SFSafariViewController.present(url: url) - } + action: @escaping ((URL) -> Void) ) { let textLinks = text.extractLinks() setText( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AuthFlowHelpers.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AuthFlowHelpers.swift index 7095c36796d..1cab2250c7f 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AuthFlowHelpers.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/AuthFlowHelpers.swift @@ -36,6 +36,7 @@ final class AuthFlowHelpers { url: URL, pane: FinancialConnectionsSessionManifest.NextPane, analyticsClient: FinancialConnectionsAnalyticsClient, + configuration: FinancialConnectionsSheet.Configuration, handleURL: (_ urlHost: String?, _ nextPaneOrDrawerOnSecondaryCta: String?) -> Void ) { let internalLinkToPaneId: [String: String] = [ @@ -68,7 +69,7 @@ final class AuthFlowHelpers { if url.scheme == "stripe" { handleURL(url.host, nextPaneOrDrawerOnSecondaryCta) } else { - SFSafariViewController.present(url: url) + SFSafariViewController.present(url: url, configuration: configuration) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/CloseConfirmationViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/CloseConfirmationViewController.swift index 69cf019cf4a..3e535931bd9 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/CloseConfirmationViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/CloseConfirmationViewController.swift @@ -12,10 +12,16 @@ import UIKit final class CloseConfirmationViewController: SheetViewController { private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration private let didSelectClose: () -> Void - init(appearance: FinancialConnectionsAppearance, didSelectClose: @escaping () -> Void) { + init( + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, + didSelectClose: @escaping () -> Void) + { self.appearance = appearance + self.configuration = configuration self.didSelectClose = didSelectClose super.init() } @@ -42,7 +48,8 @@ final class CloseConfirmationViewController: SheetViewController { "The subtitle/description of a sheet that appears when the user attempts to exit the bank linking screen." ), contentView: nil, - isSheet: true + isSheet: true, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift index fc0c5311eb8..4456b6ec473 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/DataAccessNoticeViewController.swift @@ -13,15 +13,18 @@ final class DataAccessNoticeViewController: SheetViewController { private let dataAccessNotice: FinancialConnectionsDataAccessNotice private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration private let didSelectUrl: (URL) -> Void init( dataAccessNotice: FinancialConnectionsDataAccessNotice, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectUrl: @escaping (URL) -> Void ) { self.dataAccessNotice = dataAccessNotice self.appearance = appearance + self.configuration = configuration self.didSelectUrl = didSelectUrl super.init() } @@ -41,6 +44,7 @@ final class DataAccessNoticeViewController: SheetViewController { connectedAccountBulletItems: connectedAccountNotice.body.bullets, secondSubtitle: dataAccessNotice.subtitle, merchantBulletItems: dataAccessNotice.body.bullets, + configuration: configuration, didSelectURL: didSelectUrl ) } else { @@ -61,7 +65,8 @@ final class DataAccessNoticeViewController: SheetViewController { title: dataAccessNotice.title, subtitle: firstSubtitle, contentView: contentView, - isSheet: true + isSheet: true, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( @@ -84,6 +89,7 @@ private func CreateConnectedAccountContentView( connectedAccountBulletItems: [FinancialConnectionsBulletPoint], secondSubtitle: String?, merchantBulletItems: [FinancialConnectionsBulletPoint], + configuration: FinancialConnectionsSheet.Configuration, didSelectURL: @escaping (URL) -> Void ) -> UIView { let verticalStackView = HitTestStackView() @@ -102,7 +108,7 @@ private func CreateConnectedAccountContentView( linkFont: .body(.mediumEmphasized), textColor: FinancialConnectionsAppearance.Colors.textDefault ) - secondSubtitleLabel.setText(secondSubtitle) + secondSubtitleLabel.setText(secondSubtitle, action: AttributedTextView.linkSelectedAction(with: configuration)) verticalStackView.addArrangedSubview(secondSubtitleLabel) } verticalStackView.addArrangedSubview( @@ -200,6 +206,7 @@ private struct DataAccessNoticeViewControllerRepresentable: UIViewControllerRepr DataAccessNoticeViewController( dataAccessNotice: dataAccessNotice, appearance: .stripe, + configuration: .init(), didSelectUrl: { _ in }) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/GenericInfoScreen/GenericInfoViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/GenericInfoScreen/GenericInfoViewController.swift index 231e9aa703f..b83009dca2a 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/GenericInfoScreen/GenericInfoViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/GenericInfoScreen/GenericInfoViewController.swift @@ -12,6 +12,7 @@ final class GenericInfoViewController: SheetViewController { private let genericInfoScreen: FinancialConnectionsGenericInfoScreen private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration private let iconView: UIView? private let didSelectPrimaryButton: (_ genericInfoViewController: GenericInfoViewController) -> Void private let didSelectSecondaryButton: ((_ genericInfoViewController: GenericInfoViewController) -> Void)? @@ -21,6 +22,7 @@ final class GenericInfoViewController: SheetViewController { init( genericInfoScreen: FinancialConnectionsGenericInfoScreen, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, panePresentationStyle: PanePresentationStyle, iconView: UIView? = nil, didSelectPrimaryButton: @escaping (_ genericInfoViewController: GenericInfoViewController) -> Void, @@ -30,6 +32,7 @@ final class GenericInfoViewController: SheetViewController { ) { self.genericInfoScreen = genericInfoScreen self.appearance = appearance + self.configuration = configuration self.iconView = iconView self.didSelectPrimaryButton = didSelectPrimaryButton self.didSelectSecondaryButton = didSelectSecondaryButton @@ -76,7 +79,8 @@ final class GenericInfoViewController: SheetViewController { body: genericInfoScreen.body, didSelectURL: didSelectURL ), - isSheet: (panePresentationStyle == .sheet) + isSheet: (panePresentationStyle == .sheet), + configuration: configuration ), footerView: GenericInfoFooterView( footer: genericInfoScreen.footer, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/LegalDetailsNoticeViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/LegalDetailsNoticeViewController.swift index cd1df11fee5..f037d555519 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/LegalDetailsNoticeViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/LegalDetailsNoticeViewController.swift @@ -13,15 +13,18 @@ final class LegalDetailsNoticeViewController: SheetViewController { private let legalDetailsNotice: FinancialConnectionsLegalDetailsNotice private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration private let didSelectUrl: (URL) -> Void init( legalDetailsNotice: FinancialConnectionsLegalDetailsNotice, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectUrl: @escaping (URL) -> Void ) { self.legalDetailsNotice = legalDetailsNotice self.appearance = appearance + self.configuration = configuration self.didSelectUrl = didSelectUrl super.init() } @@ -46,7 +49,8 @@ final class LegalDetailsNoticeViewController: SheetViewController { appearance: appearance, didSelectURL: didSelectUrl ), - isSheet: true + isSheet: true, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift index 5ddf74ba0f4..edd1c62882d 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPDataSource.swift @@ -17,6 +17,7 @@ protocol NetworkingOTPDataSource: AnyObject { var analyticsClient: FinancialConnectionsAnalyticsClient { get } var isTestMode: Bool { get } var appearance: FinancialConnectionsAppearance { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var pane: FinancialConnectionsSessionManifest.NextPane { get } var emailAddress: String { get } @@ -35,6 +36,7 @@ final class NetworkingOTPDataSourceImplementation: NetworkingOTPDataSource { let pane: FinancialConnectionsSessionManifest.NextPane let analyticsClient: FinancialConnectionsAnalyticsClient let emailAddress: String + let configuration: FinancialConnectionsSheet.Configuration private let customEmailType: String? private let connectionsMerchantName: String? private let apiClient: any FinancialConnectionsAPI @@ -61,6 +63,7 @@ final class NetworkingOTPDataSourceImplementation: NetworkingOTPDataSource { init( otpType: String, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, emailAddress: String, customEmailType: String?, connectionsMerchantName: String?, @@ -72,6 +75,7 @@ final class NetworkingOTPDataSourceImplementation: NetworkingOTPDataSource { ) { self.otpType = otpType self.manifest = manifest + self.configuration = configuration self.emailAddress = emailAddress self.customEmailType = customEmailType self.connectionsMerchantName = connectionsMerchantName diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPView.swift index 372ecbeb96c..d00adee24b0 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/NetworkingOTPView/NetworkingOTPView.swift @@ -141,7 +141,7 @@ final class NetworkingOTPView: UIView { linkColor: FinancialConnectionsAppearance.Colors.textCritical, alignment: .center ) - errorLabel.setText(errorText) + errorLabel.setText(errorText, action: AttributedTextView.linkSelectedAction(with: dataSource.configuration)) let errorView = UIStackView( arrangedSubviews: [errorLabel] ) @@ -285,6 +285,7 @@ private struct NetowrkingOTPViewRepresentable: UIViewRepresentable { singleAccount: true, _theme: theme ), + configuration: .init(), emailAddress: "", customEmailType: nil, connectionsMerchantName: nil, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/PaneLayoutView/PaneLayoutView+Extensions.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/PaneLayoutView/PaneLayoutView+Extensions.swift index be312264ba8..a82f70abb4e 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/PaneLayoutView/PaneLayoutView+Extensions.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/PaneLayoutView/PaneLayoutView+Extensions.swift @@ -19,7 +19,8 @@ extension PaneLayoutView { subtitle: String?, headerAlignment: UIStackView.Alignment = .leading, contentView: UIView?, - isSheet: Bool = false + isSheet: Bool = false, + configuration: FinancialConnectionsSheet.Configuration ) -> UIView { let verticalStackView = UIStackView() verticalStackView.axis = .vertical @@ -29,14 +30,16 @@ extension PaneLayoutView { iconView: iconView, title: title, alignment: headerAlignment, - isSheet: isSheet + isSheet: isSheet, + configuration: configuration ) verticalStackView.addArrangedSubview(headerView) } if subtitle != nil || contentView != nil { let bodyView = createBodyView( text: subtitle, - contentView: contentView + contentView: contentView, + configuration: configuration ) verticalStackView.addArrangedSubview(bodyView) } @@ -48,7 +51,8 @@ extension PaneLayoutView { iconView: UIView?, title: String?, alignment: UIStackView.Alignment = .leading, - isSheet: Bool = false + isSheet: Bool = false, + configuration: FinancialConnectionsSheet.Configuration ) -> UIView { let headerStackView = HitTestStackView() headerStackView.axis = .vertical @@ -66,7 +70,7 @@ extension PaneLayoutView { linkFont: titleFont, textColor: FinancialConnectionsAppearance.Colors.textDefault ) - titleLabel.setText(title) + titleLabel.setText(title, action: AttributedTextView.linkSelectedAction(with: configuration)) headerStackView.addArrangedSubview(titleLabel) } @@ -90,7 +94,8 @@ extension PaneLayoutView { @available(iOSApplicationExtension, unavailable) static func createBodyView( text: String?, - contentView: UIView? + contentView: UIView?, + configuration: FinancialConnectionsSheet.Configuration ) -> UIView { let willShowDescriptionText = (text != nil) @@ -116,7 +121,7 @@ extension PaneLayoutView { linkFont: .body(.mediumEmphasized), textColor: FinancialConnectionsAppearance.Colors.textDefault ) - textLabel.setText(text) + textLabel.setText(text, action: AttributedTextView.linkSelectedAction(with: configuration)) paddingStackView.addArrangedSubview(textLabel) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/RoundedTextField.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/RoundedTextField.swift index 68dcb4f123e..92fa22960e1 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/RoundedTextField.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/RoundedTextField.swift @@ -27,6 +27,7 @@ final class RoundedTextField: UIView { private let showDoneToolbar: Bool private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration // Used to optionally add an error message // at the bottom of the text field @@ -137,10 +138,12 @@ final class RoundedTextField: UIView { placeholder: String, footerText: String? = nil, showDoneToolbar: Bool = false, - appearance: FinancialConnectionsAppearance + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration ) { self.showDoneToolbar = showDoneToolbar self.appearance = appearance + self.configuration = configuration super.init(frame: .zero) addAndPinSubview(verticalStackView) textField.placeholder = placeholder @@ -168,9 +171,9 @@ final class RoundedTextField: UIView { let footerTextLabel: UIView? if let errorText = errorText, footerText != nil { - footerTextLabel = CreateErrorLabel(text: errorText) + footerTextLabel = CreateErrorLabel(text: errorText, configuration: configuration) } else if let errorText = errorText { - footerTextLabel = CreateErrorLabel(text: errorText) + footerTextLabel = CreateErrorLabel(text: errorText, configuration: configuration) } else if let footerText = footerText { let footerLabel = AttributedLabel( font: .label(.large), @@ -253,7 +256,7 @@ extension RoundedTextField: DoneButtonToolbarDelegate { } } -private func CreateErrorLabel(text: String) -> UIView { +private func CreateErrorLabel(text: String, configuration: FinancialConnectionsSheet.Configuration) -> UIView { let errorLabel = AttributedTextView( font: .label(.small), boldFont: .label(.smallEmphasized), @@ -261,7 +264,7 @@ private func CreateErrorLabel(text: String) -> UIView { textColor: FinancialConnectionsAppearance.Colors.textCritical, linkColor: FinancialConnectionsAppearance.Colors.textCritical ) - errorLabel.setText(text) + errorLabel.setText(text, action: AttributedTextView.linkSelectedAction(with: configuration)) return errorLabel } @@ -665,7 +668,8 @@ private struct RoundedTextFieldUIViewRepresentable: UIViewRepresentable { RoundedTextField( placeholder: placeholder, footerText: footerText, - appearance: appearance + appearance: appearance, + configuration: .init() ) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/SFSafariViewController+Extensions.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/SFSafariViewController+Extensions.swift index 585e8b0855c..22a8b1cfd9a 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/SFSafariViewController+Extensions.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Shared/SFSafariViewController+Extensions.swift @@ -10,7 +10,7 @@ import SafariServices extension SFSafariViewController { - static func present(url: URL) { + static func present(url: URL, configuration: FinancialConnectionsSheet.Configuration) { guard url.scheme == "http" || url.scheme == "https", let topMostViewController = UIViewController.topMostViewController() @@ -19,6 +19,7 @@ extension SFSafariViewController { return } let safariViewController = SFSafariViewController(url: url) + configuration.style.configure(safariViewController) topMostViewController.present(safariViewController, animated: true, completion: nil) } } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessDataSource.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessDataSource.swift index beb9ca73b42..73f6a5f4c72 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessDataSource.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessDataSource.swift @@ -11,6 +11,7 @@ import Foundation protocol SuccessDataSource: AnyObject { var manifest: FinancialConnectionsSessionManifest { get } + var configuration: FinancialConnectionsSheet.Configuration { get } var linkedAccountsCount: Int { get } var saveToLinkWithStripeSucceeded: Bool? { get } var analyticsClient: FinancialConnectionsAnalyticsClient { get } @@ -22,6 +23,7 @@ protocol SuccessDataSource: AnyObject { final class SuccessDataSourceImplementation: SuccessDataSource { let manifest: FinancialConnectionsSessionManifest + let configuration: FinancialConnectionsSheet.Configuration let linkedAccountsCount: Int let saveToLinkWithStripeSucceeded: Bool? private let apiClient: any FinancialConnectionsAPI @@ -35,6 +37,7 @@ final class SuccessDataSourceImplementation: SuccessDataSource { init( manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, linkedAccountsCount: Int, saveToLinkWithStripeSucceeded: Bool?, apiClient: any FinancialConnectionsAPI, @@ -44,6 +47,7 @@ final class SuccessDataSourceImplementation: SuccessDataSource { customSuccessPaneSubCaption: String? ) { self.manifest = manifest + self.configuration = configuration self.linkedAccountsCount = linkedAccountsCount self.saveToLinkWithStripeSucceeded = saveToLinkWithStripeSucceeded self.apiClient = apiClient diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessViewController.swift index 4885489d1f1..ad363ed94d8 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/Success/SuccessViewController.swift @@ -48,7 +48,8 @@ final class SuccessViewController: UIViewController { isLinkingOneAccount: (dataSource.linkedAccountsCount == 0 || dataSource.linkedAccountsCount == 1), showSaveToLinkFailedNotice: showSaveToLinkFailedNotice ), - appearance: dataSource.manifest.appearance + appearance: dataSource.manifest.appearance, + configuration: dataSource.configuration ) contentView.addSubview(bodyView) @@ -120,7 +121,8 @@ final class SuccessViewController: UIViewController { private func CreateBodyView( title: String, subtitle: String?, - appearance: FinancialConnectionsAppearance + appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration ) -> UIView { let titleLabel = AttributedLabel( font: .heading(.extraLarge), @@ -142,7 +144,7 @@ private func CreateBodyView( textColor: FinancialConnectionsAppearance.Colors.textDefault, alignment: .center ) - subtitleLabel.setText(subtitle) + subtitleLabel.setText(subtitle, action: AttributedTextView.linkSelectedAction(with: configuration)) labelVerticalStackView.addArrangedSubview(subtitleLabel) } diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorView.swift index 93ddcc396fb..975e403a301 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorView.swift @@ -12,6 +12,7 @@ import UIKit func TerminalErrorView( allowManualEntry: Bool, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectManualEntry: @escaping () -> Void, didSelectClose: @escaping () -> Void ) -> UIView { @@ -39,7 +40,8 @@ func TerminalErrorView( ) } }(), - contentView: nil + contentView: nil, + configuration: configuration ), footerView: PaneLayoutView.createFooterView( primaryButtonConfiguration: { diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorViewController.swift index c44af995e1b..12d3acf2a51 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/TerminalError/TerminalErrorViewController.swift @@ -20,12 +20,14 @@ final class TerminalErrorViewController: UIViewController { private let error: Error private let allowManualEntry: Bool private let appearance: FinancialConnectionsAppearance + private let configuration: FinancialConnectionsSheet.Configuration weak var delegate: TerminalErrorViewControllerDelegate? - init(error: Error, allowManualEntry: Bool, appearance: FinancialConnectionsAppearance) { + init(error: Error, allowManualEntry: Bool, appearance: FinancialConnectionsAppearance, configuration: FinancialConnectionsSheet.Configuration) { self.error = error self.allowManualEntry = allowManualEntry self.appearance = appearance + self.configuration = configuration super.init(nibName: nil, bundle: nil) } @@ -41,6 +43,7 @@ final class TerminalErrorViewController: UIViewController { let terminalErrorView = TerminalErrorView( allowManualEntry: allowManualEntry, appearance: appearance, + configuration: configuration, didSelectManualEntry: { [weak self] in guard let self = self else { return } self.delegate?.terminalErrorViewControllerDidSelectManualEntry(self) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Web/ContinueStateView.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Web/ContinueStateView.swift index d574388c66b..0c168511b80 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Web/ContinueStateView.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Web/ContinueStateView.swift @@ -19,6 +19,7 @@ final class ContinueStateViews { init( institutionImageUrl: String?, appearance: FinancialConnectionsAppearance, + configuration: FinancialConnectionsSheet.Configuration, didSelectContinue: @escaping () -> Void, didSelectCancel: (() -> Void)? = nil ) { @@ -40,7 +41,8 @@ final class ContinueStateViews { "You haven't finished linking your account. Press continue to finish the process.", "Title for a label explaining that the linking process hasn't finished yet." ), - contentView: nil + contentView: nil, + configuration: configuration ) let footerViewTuple = PaneLayoutView.createFooterView( primaryButtonConfiguration: PaneLayoutView.ButtonConfiguration( diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Web/FinancialConnectionsWebFlowViewController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Web/FinancialConnectionsWebFlowViewController.swift index 8c9c3cee00a..eb82453ceeb 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Web/FinancialConnectionsWebFlowViewController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Web/FinancialConnectionsWebFlowViewController.swift @@ -38,6 +38,7 @@ final class FinancialConnectionsWebFlowViewController: UIViewController { let continueStateViews = ContinueStateViews( institutionImageUrl: nil, appearance: manifest.appearance, + configuration: configuration, didSelectContinue: { [weak self] in guard let self else { return } if let url = self.lastOpenedNativeURL { @@ -68,6 +69,7 @@ final class FinancialConnectionsWebFlowViewController: UIViewController { private let apiClient: any FinancialConnectionsAPI private let sessionFetcher: FinancialConnectionsSessionFetcher private let manifest: FinancialConnectionsSessionManifest + private let configuration: FinancialConnectionsSheet.Configuration private let returnURL: String? private let elementsSessionContext: ElementsSessionContext? private let prefillDetailsOverride: WebPrefillDetails? @@ -95,6 +97,7 @@ final class FinancialConnectionsWebFlowViewController: UIViewController { clientSecret: String, apiClient: any FinancialConnectionsAPI, manifest: FinancialConnectionsSessionManifest, + configuration: FinancialConnectionsSheet.Configuration, sessionFetcher: FinancialConnectionsSessionFetcher, returnURL: String?, elementsSessionContext: ElementsSessionContext?, @@ -103,6 +106,7 @@ final class FinancialConnectionsWebFlowViewController: UIViewController { self.clientSecret = clientSecret self.apiClient = apiClient self.manifest = manifest + self.configuration = configuration self.sessionFetcher = sessionFetcher self.returnURL = returnURL self.elementsSessionContext = elementsSessionContext