From 2d70964223f9323858d11e72674fc66cf98e361f Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Wed, 14 Sep 2022 14:02:52 +0200 Subject: [PATCH] Implement message sending --- .../AppFeature/AppEnvironment+Live.swift | 5 +++ .../Sources/ChatFeature/ChatFeature.swift | 41 ++++++++++++++++++- .../Sources/ChatFeature/ChatView.swift | 11 +++-- .../ChatFeatureTests/ChatFeatureTests.swift | 32 +++++++++++++++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 93a84443..611123e0 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -70,6 +70,11 @@ extension AppEnvironment { ChatEnvironment( messenger: messenger, db: dbManager.getDB, + sendMessage: .live( + messenger: messenger, + db: dbManager.getDB, + now: Date.init + ), mainQueue: mainQueue, bgQueue: bgQueue ) diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift index af1554f0..26757d71 100644 --- a/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift +++ b/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift @@ -1,4 +1,5 @@ import AppCore +import Combine import ComposableArchitecture import Foundation import XCTestDynamicOverlay @@ -34,40 +35,48 @@ public struct ChatState: Equatable, Identifiable { id: ID, myContactId: Data? = nil, messages: IdentifiedArrayOf<Message> = [], - failure: String? = nil + failure: String? = nil, + text: String = "" ) { self.id = id self.myContactId = myContactId self.messages = messages self.failure = failure + self.text = text } public var id: ID public var myContactId: Data? public var messages: IdentifiedArrayOf<Message> public var failure: String? + @BindableState public var text: String } -public enum ChatAction: Equatable { +public enum ChatAction: Equatable, BindableAction { case start case didFetchMessages(IdentifiedArrayOf<ChatState.Message>) + case sendTapped + case binding(BindingAction<ChatState>) } public struct ChatEnvironment { public init( messenger: Messenger, db: DBManagerGetDB, + sendMessage: SendMessage, mainQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue> ) { self.messenger = messenger self.db = db + self.sendMessage = sendMessage self.mainQueue = mainQueue self.bgQueue = bgQueue } public var messenger: Messenger public var db: DBManagerGetDB + public var sendMessage: SendMessage public var mainQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue> } @@ -77,6 +86,7 @@ extension ChatEnvironment { public static let unimplemented = ChatEnvironment( messenger: .unimplemented, db: .unimplemented, + sendMessage: .unimplemented, mainQueue: .unimplemented, bgQueue: .unimplemented ) @@ -126,5 +136,32 @@ public let chatReducer = Reducer<ChatState, ChatAction, ChatEnvironment> case .didFetchMessages(let messages): state.messages = messages return .none + + case .sendTapped: + let text = state.text + let chatId = state.id + state.text = "" + return Effect.run { subscriber in + switch chatId { + case .contact(let recipientId): + env.sendMessage( + text: text, + to: recipientId, + onError: { error in + // TODO: handle error + print("^^^ ERROR: \(error)") + } + ) + } + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .binding(_): + return .none } } +.binding() diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift index 74e08c7b..5905931f 100644 --- a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift +++ b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift @@ -13,11 +13,13 @@ public struct ChatView: View { var myContactId: Data? var messages: IdentifiedArrayOf<ChatState.Message> var failure: String? + var text: String init(state: ChatState) { myContactId = state.myContactId messages = state.messages failure = state.failure + text = state.text } } @@ -52,11 +54,14 @@ public struct ChatView: View { VStack(spacing: 0) { Divider() HStack { - TextField("Text", text: .constant("")) - .textFieldStyle(.roundedBorder) + TextField("Text", text: viewStore.binding( + get: \.text, + send: { ChatAction.set(\.$text, $0) } + )) + .textFieldStyle(.roundedBorder) Button { - + viewStore.send(.sendTapped) } label: { Image(systemName: "paperplane.fill") } diff --git a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift index 3e812db2..89f0658d 100644 --- a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift +++ b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift @@ -130,4 +130,36 @@ final class ChatFeatureTests: XCTestCase { $0.failure = error.localizedDescription } } + + func testSend() { + struct SendMessageParams: Equatable { + var text: String + var recipientId: Data + } + var didSendMessageWithParams: [SendMessageParams] = [] + + let store = TestStore( + initialState: ChatState(id: .contact("contact-id".data(using: .utf8)!)), + reducer: chatReducer, + environment: .unimplemented + ) + + store.environment.mainQueue = .immediate + store.environment.bgQueue = .immediate + store.environment.sendMessage.run = { text, recipientId, _ in + didSendMessageWithParams.append(.init(text: text, recipientId: recipientId)) + } + + store.send(.set(\.$text, "Hello")) { + $0.text = "Hello" + } + + store.send(.sendTapped) { + $0.text = "" + } + + XCTAssertNoDifference(didSendMessageWithParams, [ + .init(text: "Hello", recipientId: "contact-id".data(using: .utf8)!) + ]) + } } -- GitLab