|
| 1 | +import ComposableArchitecture |
| 2 | +import Dependencies |
1 | 3 | import Foundation |
2 | 4 | import SwiftUI |
3 | | -import Dependencies |
4 | 5 |
|
5 | 6 | public enum ToastType { |
6 | 7 | case info |
@@ -28,14 +29,12 @@ public extension DependencyValues { |
28 | 29 | get { self[ToastControllerDependencyKey.self] } |
29 | 30 | set { self[ToastControllerDependencyKey.self] = newValue } |
30 | 31 | } |
31 | | - |
32 | | - var toast: (String, ToastType) -> Void { |
33 | | - get { toastController.toast } |
34 | | - } |
| 32 | + |
| 33 | + var toast: (String, ToastType) -> Void { toastController.toast } |
35 | 34 | } |
36 | 35 |
|
37 | 36 | public class ToastController: ObservableObject { |
38 | | - public struct Message: Identifiable { |
| 37 | + public struct Message: Identifiable, Equatable { |
39 | 38 | public var id: UUID |
40 | 39 | public var type: ToastType |
41 | 40 | public var content: Text |
@@ -69,3 +68,50 @@ public class ToastController: ObservableObject { |
69 | 68 | } |
70 | 69 | } |
71 | 70 |
|
| 71 | +public struct Toast: ReducerProtocol { |
| 72 | + public typealias Message = ToastController.Message |
| 73 | + public struct State: Equatable { |
| 74 | + public var messages: [Message] = [] |
| 75 | + } |
| 76 | + |
| 77 | + public enum Action: Equatable { |
| 78 | + case appear |
| 79 | + case updateMessages([Message]) |
| 80 | + case toast(String, ToastType) |
| 81 | + } |
| 82 | + |
| 83 | + @Dependency(\.toastController) var toastController |
| 84 | + |
| 85 | + struct CancelID: Hashable {} |
| 86 | + |
| 87 | + public init() {} |
| 88 | + |
| 89 | + public var body: some ReducerProtocol<State, Action> { |
| 90 | + Reduce { state, action in |
| 91 | + switch action { |
| 92 | + case .appear: |
| 93 | + return .run { send in |
| 94 | + let stream = AsyncStream<[Message]> { continuation in |
| 95 | + let cancellable = toastController.$messages.sink { newValue in |
| 96 | + continuation.yield(newValue) |
| 97 | + } |
| 98 | + continuation.onTermination = { _ in |
| 99 | + cancellable.cancel() |
| 100 | + } |
| 101 | + } |
| 102 | + for await newValue in stream { |
| 103 | + try Task.checkCancellation() |
| 104 | + await send(.updateMessages(newValue)) |
| 105 | + } |
| 106 | + }.cancellable(id: CancelID(), cancelInFlight: true) |
| 107 | + case let .updateMessages(messages): |
| 108 | + state.messages = messages |
| 109 | + return .none |
| 110 | + case let .toast(content, type): |
| 111 | + toastController.toast(content: content, type: type) |
| 112 | + return .none |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | + |
0 commit comments