Sfoglia il codice sorgente

Implement support to present action sheets from coordinators

Pavel Yurchenko 1 anno fa
parent
commit
0bf82341ce

+ 25 - 8
CoordinatorSUI/Sources/CoordinatorSUI/AlertPresenter.swift

@@ -9,15 +9,29 @@ import SwiftUI
 
 public final class AlertPresenter: ObservableObject {
 
-    private struct AlertConfiguration {
+    public struct AlertConfiguration {
         let title: String
         let buttons: [AlertButton]
     }
 
-    public struct AlertButton {
+    public struct AlertButton: Hashable {
         let title: String
-        let role: ButtonRole
+        let role: ButtonRole?
         let action: Action
+
+        public init(title: String, role: ButtonRole? = nil, action: @escaping Action) {
+            self.title = title
+            self.role = role
+            self.action = action
+        }
+
+        public static func == (lhs: AlertButton, rhs: AlertButton) -> Bool {
+            lhs.title == rhs.title
+        }
+
+        public func hash(into hasher: inout Hasher) {
+            hasher.combine(title)
+        }
     }
 
     private var alertConfiguration = AlertConfiguration(title: "", buttons: [])
@@ -33,17 +47,20 @@ public final class AlertPresenter: ObservableObject {
 
     @ViewBuilder
     public func buildButtons() -> some View {
-        ForEach(alertConfiguration.buttons.indices) { index in
-            let button = self.alertConfiguration.buttons[index]
-            Button(button.title, role: button.role, action: button.action)
+        ForEach(alertConfiguration.buttons, id: \.self) {
+            Button($0.title, role: $0.role, action: $0.action)
         }
     }
 
-    public func showAlert(_ title: String, buttons: [AlertButton]) {
-        self.alertConfiguration = AlertConfiguration(title: title, buttons: buttons)
+    public func showAlert(with configuration: AlertConfiguration) {
+        self.alertConfiguration = configuration
         self.isPresenting = true
     }
 
+    public func showAlert(_ title: String, buttons: [AlertButton]) {
+        showAlert(with: AlertConfiguration(title: title, buttons: buttons))
+    }
+
     public func showSimpleAlert(_ title: String, action: @escaping Action) {
         showAlert(title, buttons: [AlertButton(title: "OK", role: .cancel, action: action)])
     }

+ 4 - 0
CoordinatorSUI/Sources/CoordinatorSUI/BaseCoordinator.swift

@@ -19,6 +19,10 @@ open class BaseCoordinator: Coordinator, ObservableObject {
         childCoordinators.last
     }
 
+    public let alertPresenter = AlertPresenter()
+
+    public let confirmationDialogPresenter = ConfirmationDialogPresenter()
+
     // MARK: - Private properties
 
     private var childCoordinators = [Coordinator]()

+ 63 - 0
CoordinatorSUI/Sources/CoordinatorSUI/ConfirmationDialogPresenter.swift

@@ -0,0 +1,63 @@
+//
+//  ConfirmationDialogPresenter.swift
+//  CoordinatorSUI
+//
+//  Created by Pavel Yurchenko on 05.12.2024.
+//
+
+import SwiftUI
+
+public final class ConfirmationDialogPresenter: ObservableObject {
+
+    public struct DialogConfiguration {
+        let title: String
+        let buttons: [DialogButton]
+    }
+
+    public struct DialogButton: Hashable {
+        let title: String
+        let role: ButtonRole?
+        let action: Action
+
+        public init(title: String, role: ButtonRole? = nil, action: @escaping Action) {
+            self.title = title
+            self.role = role
+            self.action = action
+        }
+
+        public static func == (lhs: ConfirmationDialogPresenter.DialogButton, rhs: ConfirmationDialogPresenter.DialogButton) -> Bool {
+            lhs.title == rhs.title
+        }
+
+        public func hash(into hasher: inout Hasher) {
+            hasher.combine(title)
+        }
+    }
+
+    private var dialogConfiguration = DialogConfiguration(title: "", buttons: [])
+
+    @Published
+    public var isPresenting: Bool = false
+
+    public var title: String {
+        dialogConfiguration.title
+    }
+
+    public init() { }
+
+    @ViewBuilder
+    public func buildButtons() -> some View {
+        ForEach(dialogConfiguration.buttons, id: \.self) {
+            Button($0.title, role: $0.role, action: $0.action)
+        }
+    }
+
+    public func showDialog(with configuration: DialogConfiguration) {
+        self.dialogConfiguration = configuration
+        self.isPresenting = true
+    }
+
+    public func showDialog(_ title: String, buttons: [DialogButton]) {
+        showDialog(with: DialogConfiguration(title: title, buttons: buttons))
+    }
+}

+ 0 - 2
CoordinatorSUIExamples/CoordinatorSUIExamples/Flows/ModalProductsCoordinator.swift

@@ -10,8 +10,6 @@ import SwiftUI
 
 final class ModalProductsCoordinator: BaseCoordinator {
 
-    var alertPresenter = AlertPresenter()
-
     func run() -> some View {
         showProductsModule()
     }

+ 19 - 2
CoordinatorSUIExamples/CoordinatorSUIExamples/Flows/Tabs/MainCoordinator.swift

@@ -13,8 +13,6 @@ final class MainCoordinator: BaseCoordinator {
     @Published
     var presentation: Route?
 
-    var alertPresenter = AlertPresenter()
-
     func run() -> some View {
         showMainModule()
     }
@@ -73,6 +71,19 @@ final class MainCoordinator: BaseCoordinator {
                 },
                 onModalCart: { [weak self] in
                     self?.presentation = .cart
+                },
+                onConfirmationDialog: { [weak self] in
+                    self?.confirmationDialogPresenter.showDialog(
+                        "Are you sure?",
+                        buttons: [
+                            .init(title: "Yes", action: {
+                                print("OnYES")
+                            }),
+                            .init(title: "No", role: .cancel, action: {
+                                print("OnNO")
+                            })
+                        ]
+                    )
                 }
             )
         )
@@ -137,11 +148,13 @@ struct MainNavigationView: View {
     @StateObject private var coordinator: MainCoordinator
     @StateObject private var router: Router
     @StateObject private var alertPresenter: AlertPresenter
+    @StateObject private var confirmationDialogPresenter: ConfirmationDialogPresenter
 
     init(_ coordinator: MainCoordinator) {
         _coordinator = StateObject(wrappedValue: coordinator)
         _router = StateObject(wrappedValue: coordinator.currentRouter)
         _alertPresenter = StateObject(wrappedValue: coordinator.alertPresenter)
+        _confirmationDialogPresenter = StateObject(wrappedValue: coordinator.confirmationDialogPresenter)
     }
 
     var body: some View {
@@ -159,6 +172,10 @@ struct MainNavigationView: View {
             alertPresenter.title,
             isPresented: $alertPresenter.isPresenting,
             actions: alertPresenter.buildButtons
+        ).confirmationDialog(
+            confirmationDialogPresenter.title,
+            isPresented: $confirmationDialogPresenter.isPresenting,
+            actions: confirmationDialogPresenter.buildButtons
         )
     }
 }

+ 1 - 0
CoordinatorSUIExamples/CoordinatorSUIExamples/Modules/Main/MainVM.swift

@@ -20,6 +20,7 @@ final class MainVM {
         var onFeedback: Action?
         var onModalProducts: Action?
         var onModalCart: Action?
+        var onConfirmationDialog: Action?
     }
 
     let output: Output

+ 3 - 0
CoordinatorSUIExamples/CoordinatorSUIExamples/Modules/Main/MainView.swift

@@ -30,6 +30,9 @@ struct MainView: View {
             Button("Modal Cart") {
                 vm.output.onModalCart?()
             }
+            Button("Confirmation Dialog") {
+                vm.output.onConfirmationDialog?()
+            }
         }
         .padding()
     }