From b00b9bdd46dde369e3e6b14f8731800f57b7d3fd Mon Sep 17 00:00:00 2001 From: Mel Ludowise Date: Wed, 25 Sep 2024 23:24:18 -0700 Subject: [PATCH] NotificationBanner --- .../StripeConnect.xcodeproj/project.pbxproj | 8 ++ .../Source/Components/ComponentType.swift | 1 + .../NotificationBannerViewController.swift | 84 +++++++++++++++++++ .../Source/EmbeddedComponentManager.swift | 10 ++- .../OnNotificationsChangeHandler.swift | 19 +++++ 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 StripeConnect/StripeConnect/Source/Components/NotificationBannerViewController.swift create mode 100644 StripeConnect/StripeConnect/Source/Internal/Webview/MessageHandlers/OnNotificationsChangeHandler.swift diff --git a/StripeConnect/StripeConnect.xcodeproj/project.pbxproj b/StripeConnect/StripeConnect.xcodeproj/project.pbxproj index cee0a516ea7..3f9030b474b 100644 --- a/StripeConnect/StripeConnect.xcodeproj/project.pbxproj +++ b/StripeConnect/StripeConnect.xcodeproj/project.pbxproj @@ -89,6 +89,8 @@ 41D17A6E2C5A7429007C6EE6 /* Version.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 41D17A632C5A7429007C6EE6 /* Version.xcconfig */; }; E65691222CA52D5900E0DB00 /* StripeConnect+Exports.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */; }; E65691202CA5248300E0DB00 /* AccountManagementViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E656911F2CA5248300E0DB00 /* AccountManagementViewControllerTests.swift */; }; + E65691252CA52F9D00E0DB00 /* NotificationBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691232CA52F8600E0DB00 /* NotificationBannerViewController.swift */; }; + E65691272CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691262CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift */; }; E6C5F5F62C9FEE0200861709 /* AccountManagementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C5F5F52C9FEE0200861709 /* AccountManagementViewController.swift */; }; E6F485F82C9E35A5000D914F /* PaymentDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F485F72C9E35A5000D914F /* PaymentDetailsViewController.swift */; }; E6F485FC2C9E360A000D914F /* ConnectJSURLParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F485FB2C9E360A000D914F /* ConnectJSURLParams.swift */; }; @@ -190,6 +192,8 @@ 41D17A632C5A7429007C6EE6 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StripeConnect+Exports.swift"; sourceTree = ""; }; E656911F2CA5248300E0DB00 /* AccountManagementViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewControllerTests.swift; sourceTree = ""; }; + E65691232CA52F8600E0DB00 /* NotificationBannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBannerViewController.swift; sourceTree = ""; }; + E65691262CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnNotificationsChangeHandler.swift; sourceTree = ""; }; E6C5F5F52C9FEE0200861709 /* AccountManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewController.swift; sourceTree = ""; }; E6F485F72C9E35A5000D914F /* PaymentDetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentDetailsViewController.swift; sourceTree = ""; }; E6F485FB2C9E360A000D914F /* ConnectJSURLParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectJSURLParams.swift; sourceTree = ""; }; @@ -239,6 +243,7 @@ 413987DC2C640A29001D375E /* FetchInitParamsMessageHandler.swift */, 4186664B2C66AC8C003DB62E /* OnLoaderStartMessageHandler.swift */, 4186664D2C66ACB3003DB62E /* OnLoadErrorMessageHandler.swift */, + E65691262CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift */, 413987E02C641688001D375E /* PageDidLoadMessageHandler.swift */, 410D0FCB2C6CFFDB009B0E26 /* AccountSessionClaimedMessageHandler.swift */, 410D0FCD2C6D000B009B0E26 /* OpenAuthenticatedWebViewMessageHandler.swift */, @@ -315,6 +320,7 @@ E6C5F5F52C9FEE0200861709 /* AccountManagementViewController.swift */, 4171B1582C9A5EEC00547F7D /* AccountOnboardingViewController.swift */, 416E9E832C76AE0900A0B917 /* ComponentType.swift */, + E65691232CA52F8600E0DB00 /* NotificationBannerViewController.swift */, E6F485F72C9E35A5000D914F /* PaymentDetailsViewController.swift */, 416E9E852C76B35E00A0B917 /* PayoutsViewController.swift */, ); @@ -648,9 +654,11 @@ 413987CE2C63F34B001D375E /* UpdateConnectInstanceSender.swift in Sources */, 416E9ED42C77F90600A0B917 /* WKScriptMessage+extension.swift in Sources */, E65691222CA52D5900E0DB00 /* StripeConnect+Exports.swift in Sources */, + E65691252CA52F9D00E0DB00 /* NotificationBannerViewController.swift in Sources */, 410D0FE32C6D31C6009B0E26 /* StripeConnectConstants.swift in Sources */, 4186664A2C66AC66003DB62E /* OnSetterFunctionCalledMessageHandler.swift in Sources */, 413987CC2C63F34B001D375E /* VoidPayload.swift in Sources */, + E65691272CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift in Sources */, 413987DD2C640A29001D375E /* FetchInitParamsMessageHandler.swift in Sources */, 413987D42C640848001D375E /* CallSetterWithSerializableValueSender.swift in Sources */, 416E9E742C751A1A00A0B917 /* ConnectComponentWebView.swift in Sources */, diff --git a/StripeConnect/StripeConnect/Source/Components/ComponentType.swift b/StripeConnect/StripeConnect/Source/Components/ComponentType.swift index af946f765b4..a80260a36d1 100644 --- a/StripeConnect/StripeConnect/Source/Components/ComponentType.swift +++ b/StripeConnect/StripeConnect/Source/Components/ComponentType.swift @@ -16,4 +16,5 @@ enum ComponentType: String, Encodable { case onboarding = "account-onboarding" /// Show details of a given payment and allow users to manage disputes and perform refunds. case paymentDetails = "payment-details" + case notificationBanner = "notification-banner" } diff --git a/StripeConnect/StripeConnect/Source/Components/NotificationBannerViewController.swift b/StripeConnect/StripeConnect/Source/Components/NotificationBannerViewController.swift new file mode 100644 index 00000000000..6c9d9e69f20 --- /dev/null +++ b/StripeConnect/StripeConnect/Source/Components/NotificationBannerViewController.swift @@ -0,0 +1,84 @@ +// +// NotificationBannerViewController.swift +// StripeConnectTests +// +// Created by Mel Ludowise on 9/25/24. +// + +import UIKit + +@_spi(DashboardOnly) +@available(iOS 15, *) +public class NotificationBannerViewController: UIViewController { + let webView: ConnectComponentWebView + + public weak var delegate: NotificationBannerViewControllerDelegate? + + init(componentManager: EmbeddedComponentManager, + collectionOptions: AccountCollectionOptions) { + webView = ConnectComponentWebView( + componentManager: componentManager, + componentType: .notificationBanner + ) + super.init(nibName: nil, bundle: nil) + + webView.addMessageHandler(OnLoadErrorMessageHandler { [weak self] value in + guard let self else { return } + self.delegate?.notificationBanner(self, didFailLoadWithError: value.error.connectEmbedError) + }) + webView.addMessageHandler(OnNotificationsChangeHandler { [weak self] value in + guard let self else { return } + self.delegate?.notificationBanner(self, didChangeWithTotal: value.total, andActionRequired: value.actionRequired) + }) + + // TODO(MXMOBILE-2796): Send collection options to web view + + webView.presentPopup = { [weak self] vc in + self?.present(vc, animated: true) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func loadView() { + view = webView + } +} + +@_spi(DashboardOnly) +@available(iOS 15, *) +public protocol NotificationBannerViewControllerDelegate: AnyObject { + /** + Triggered when an error occurs loading the notification banner component + - Parameters: + - notificationBanner: The notification banner component that errored when loading + - error: The error that occurred when loading the component + */ + func notificationBanner(_ notificationBanner: NotificationBannerViewController, + didFailLoadWithError error: Error) + + /** + Triggered when the total number of notifications or notifications with required actions updates + - Parameters: + - notificationBanner: The notification banner component that changed + - total: The total number of notifications in the banner + - actionRequired: The number of notifications that require user action + */ + func notificationBanner(_ notificationBanner: NotificationBannerViewController, + didChangeWithTotal total: Int, + andActionRequired actionRequired: Int) +} + +@_spi(DashboardOnly) +@available(iOS 15, *) +public extension NotificationBannerViewControllerDelegate { + // Default implementation to make optional + func notificationBanner(_ notificationBanner: NotificationBannerViewController, + didFailLoadWithError error: Error) { } + + func notificationBanner(_ notificationBanner: NotificationBannerViewController, + didChangeWithTotal total: Int, + andActionRequired actionRequired: Int) { } +} diff --git a/StripeConnect/StripeConnect/Source/EmbeddedComponentManager.swift b/StripeConnect/StripeConnect/Source/EmbeddedComponentManager.swift index e2eacdbbe15..eb9fc99a3f1 100644 --- a/StripeConnect/StripeConnect/Source/EmbeddedComponentManager.swift +++ b/StripeConnect/StripeConnect/Source/EmbeddedComponentManager.swift @@ -100,11 +100,19 @@ public class EmbeddedComponentManager { @_spi(DashboardOnly) public func createAccountManagementViewController( - collectionOptions: AccountCollectionOptions = .init()) -> AccountManagementViewController { + collectionOptions: AccountCollectionOptions = .init() + ) -> AccountManagementViewController { .init(componentManager: self, collectionOptions: collectionOptions) } + @_spi(DashboardOnly) + public func createNotificationBannerViewController( + collectionOptions: AccountCollectionOptions = .init() + ) -> NotificationBannerViewController { + .init(componentManager: self, collectionOptions: collectionOptions) + } + /// Used to keep reference of all web views associated with this component manager. /// - Parameters: /// - webView: The web view associated with this component manager diff --git a/StripeConnect/StripeConnect/Source/Internal/Webview/MessageHandlers/OnNotificationsChangeHandler.swift b/StripeConnect/StripeConnect/Source/Internal/Webview/MessageHandlers/OnNotificationsChangeHandler.swift new file mode 100644 index 00000000000..e241adc602b --- /dev/null +++ b/StripeConnect/StripeConnect/Source/Internal/Webview/MessageHandlers/OnNotificationsChangeHandler.swift @@ -0,0 +1,19 @@ +// +// OnNotificationsChangeHandler.swift +// StripeConnect +// +// Created by Mel Ludowise on 9/25/24. +// + +import Foundation + +class OnNotificationsChangeHandler: OnSetterFunctionCalledMessageHandler.Handler { + struct Values: Codable, Equatable { + let total: Int + let actionRequired: Int + } + + init(didReceiveMessage: @escaping (Values) -> Void) { + super.init(setter: "setOnNotificationsChange", didReceiveMessage: didReceiveMessage) + } +}