Newer
Older
import ComposableArchitecture
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
public struct ChatState: Equatable, Identifiable {
public enum ID: Equatable, Hashable {
case contact(Data)
}
public struct Message: Equatable, Identifiable {
public init(
text: String,
status: XXModels.Message.Status
) {
self.id = id
self.date = date
self.senderId = senderId
self.text = text
public var date: Date
public var senderId: Data
public var text: String
public var status: XXModels.Message.Status
}
public init(
id: ID,
myContactId: Data? = nil,
messages: IdentifiedArrayOf<Message> = [],
self.myContactId = myContactId
self.messages = messages
public var myContactId: Data?
public var messages: IdentifiedArrayOf<Message>
public enum ChatAction: Equatable, BindableAction {
case didFetchMessages(IdentifiedArrayOf<ChatState.Message>)
case sendTapped
case binding(BindingAction<ChatState>)
public init(
messenger: Messenger,
db: DBManagerGetDB,
mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>
) {
self.messenger = messenger
self.db = db
self.mainQueue = mainQueue
self.bgQueue = bgQueue
}
public var messenger: Messenger
public var db: DBManagerGetDB
public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue>
}
#if DEBUG
extension ChatEnvironment {
public static let unimplemented = ChatEnvironment(
messenger: .unimplemented,
db: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented
)
}
#endif
public let chatReducer = Reducer<ChatState, ChatAction, ChatEnvironment>
{ state, action, env in
state.failure = nil
do {
let myContactId = try env.messenger.e2e.tryGet().getContact().getId()
state.myContactId = myContactId
let queryChat: XXModels.Message.Query.Chat
switch state.id {
case .contact(let contactId):
queryChat = .direct(myContactId, contactId)
}
let query = XXModels.Message.Query(chat: queryChat)
return try env.db().fetchMessagesPublisher(query)
.assertNoFailure()
.map { messages in
messages.compactMap { message in
guard let id = message.id else { return nil }
return ChatState.Message(
id: id,
date: message.date,
senderId: message.senderId,
text: message.text,
status: message.status
)
}
}
.map { IdentifiedArrayOf<ChatState.Message>(uniqueElements: $0) }
.map(ChatAction.didFetchMessages)
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
.cancellable(id: FetchEffectId.self, cancelInFlight: true)
} catch {
state.failure = error.localizedDescription
return .none
}
case .didFetchMessages(let messages):
state.messages = messages
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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