wip
This commit is contained in:
parent
9f91741354
commit
8ce21712b7
|
@ -10,4 +10,5 @@ enum AppAction: Codable {
|
||||||
case rostersAction(_ action: RostersAction)
|
case rostersAction(_ action: RostersAction)
|
||||||
case chatsAction(_ action: ChatsAction)
|
case chatsAction(_ action: ChatsAction)
|
||||||
case conversationAction(_ action: ConversationAction)
|
case conversationAction(_ action: ConversationAction)
|
||||||
|
case messagesAction(_ action: MessagesAction)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
enum MessagesAction: Codable {
|
||||||
|
case newMessageReceived(Message)
|
||||||
|
case messageDraftUpdate(Message)
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ extension Database {
|
||||||
table.column("toJid", .text).notNull()
|
table.column("toJid", .text).notNull()
|
||||||
table.column("timestamp", .datetime).notNull()
|
table.column("timestamp", .datetime).notNull()
|
||||||
table.column("body", .text)
|
table.column("body", .text)
|
||||||
|
table.column("type", .text).notNull()
|
||||||
// table.column("isReaded", .boolean).notNull().defaults(to: false)
|
// table.column("isReaded", .boolean).notNull().defaults(to: false)
|
||||||
// table.column("subject", .text)
|
// table.column("subject", .text)
|
||||||
// table.column("threadId", .text)
|
// table.column("threadId", .text)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class MessagesMiddleware {
|
||||||
|
static let shared = MessagesMiddleware()
|
||||||
|
|
||||||
|
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,26 @@ final class XMPPMiddleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
service.clientMessages.sink { client, martinMessage in
|
||||||
|
print("---")
|
||||||
|
print("Message received: \(martinMessage)")
|
||||||
|
print("In client: \(client)")
|
||||||
|
print("---")
|
||||||
|
// guard let message = Message.mapMartinMessage(martinMessage) else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if message.type == .writingProcessUpdate {
|
||||||
|
// DispatchQueue.main.async {
|
||||||
|
// store.dispatch(.messagesAction(.messageDraftUpdate(message)))
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// DispatchQueue.main.async {
|
||||||
|
// store.dispatch(.messagesAction(.newMessageReceived(message)))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
|
|
@ -1,30 +1,98 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import GRDB
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
enum MessageType: String, Codable, DatabaseValueConvertible {
|
enum MessageType: String, Codable, DatabaseValueConvertible {
|
||||||
|
case chat
|
||||||
|
case channel
|
||||||
|
case groupchat
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageContentType: String, Codable, DatabaseValueConvertible {
|
||||||
case text
|
case text
|
||||||
case image
|
case image
|
||||||
case video
|
case video
|
||||||
case audio
|
case audio
|
||||||
case file
|
case file
|
||||||
case location
|
case location
|
||||||
|
case typing
|
||||||
|
case invite
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Message: DBStorable, Equatable {
|
struct MessageContainer: Stateable, DatabaseValueConvertible {
|
||||||
static let databaseTableName = "messages"
|
|
||||||
|
|
||||||
let id: String
|
let id: String
|
||||||
let chatId: String
|
let type: MessageType
|
||||||
let fromJid: String
|
let content: any MessageContent
|
||||||
let toJid: String
|
|
||||||
let timestamp: Date
|
|
||||||
let body: String?
|
|
||||||
// var isReaded: Bool
|
|
||||||
// let subject: String?
|
|
||||||
// let threadId: String?
|
|
||||||
// let errorType: String?
|
|
||||||
|
|
||||||
var type: MessageType {
|
|
||||||
.text
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol MessageContent: Stateable, DatabaseValueConvertible {
|
||||||
|
var type: MessageContentType { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// enum MessageType: String, Codable, DatabaseValueConvertible {
|
||||||
|
// case text
|
||||||
|
// case image
|
||||||
|
// case video
|
||||||
|
// case audio
|
||||||
|
// case file
|
||||||
|
// case location
|
||||||
|
// case writingProcessUpdate
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// struct Message: DBStorable, Equatable {
|
||||||
|
// static let databaseTableName = "messages"
|
||||||
|
//
|
||||||
|
// let id: String
|
||||||
|
// let chatId: String
|
||||||
|
// let fromJid: String
|
||||||
|
// let toJid: String
|
||||||
|
// let timestamp: Date
|
||||||
|
// let body: String?
|
||||||
|
// let type: MessageType
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Special extnesion for mapping Martin.Message to Message
|
||||||
|
// extension Message {
|
||||||
|
// static func mapMartinMessage(_ martinMessage: Martin.Message) -> Message? {
|
||||||
|
// // for draft messages
|
||||||
|
// if martinMessage.hints.contains(.noStore) {
|
||||||
|
// return Message(
|
||||||
|
// id: martinMessage.id ?? UUID().uuidString,
|
||||||
|
// chatId: "none", // chat id will be filled later
|
||||||
|
// fromJid: martinMessage.from?.bareJid.stringValue ?? "",
|
||||||
|
// toJid: martinMessage.to?.bareJid.stringValue ?? "",
|
||||||
|
// timestamp: Date(),
|
||||||
|
// body: nil,
|
||||||
|
// type: .writingProcessUpdate
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // if regular message contains no body - return nil
|
||||||
|
// guard let body = martinMessage.body else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// print("Message received: \(martinMessage)")
|
||||||
|
// print("From: \(martinMessage.from)")
|
||||||
|
// print("To: \(martinMessage.to)")
|
||||||
|
// print("Body: \(martinMessage.body)")
|
||||||
|
// print("Type: \(martinMessage.type)")
|
||||||
|
// print("Id: \(martinMessage.id)")
|
||||||
|
// print("Subject: \(martinMessage.subject)")
|
||||||
|
// print("----")
|
||||||
|
// print("!!!!!-----Message body: \(body)")
|
||||||
|
//
|
||||||
|
// // parse regular message
|
||||||
|
// return nil
|
||||||
|
// // Message(
|
||||||
|
// // id: message.id,
|
||||||
|
// // chatId: message.chatId,
|
||||||
|
// // fromJid: message.from,
|
||||||
|
// // toJid: message.to,
|
||||||
|
// // timestamp: message.timestamp,
|
||||||
|
// // body: message.body,
|
||||||
|
// // type: MessageType(rawValue: message.type) ?? .text
|
||||||
|
// // )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
@ -27,6 +27,9 @@ extension AppState {
|
||||||
|
|
||||||
case .conversationAction(let action):
|
case .conversationAction(let action):
|
||||||
ConversationState.reducer(state: &state.conversationsState, action: action)
|
ConversationState.reducer(state: &state.conversationsState, action: action)
|
||||||
|
|
||||||
|
case .messagesAction:
|
||||||
|
break // messages actions are processed by MessagesMiddleware, and other components
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
struct ConversationState: Stateable {
|
struct ConversationState: Stateable {
|
||||||
var currentChat: Chat?
|
var currentChat: Chat?
|
||||||
var dumb: Bool
|
var currentMessages: [Message]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Init
|
// MARK: Init
|
||||||
extension ConversationState {
|
extension ConversationState {
|
||||||
init() {
|
init() {
|
||||||
dumb = false
|
currentMessages = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,19 @@ protocol MartinsManager: Martin.RosterManager & Martin.ChatManager {}
|
||||||
final class XMPPService: ObservableObject {
|
final class XMPPService: ObservableObject {
|
||||||
private let manager: MartinsManager
|
private let manager: MartinsManager
|
||||||
private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>()
|
private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>()
|
||||||
private var clientStateCancellables: [AnyCancellable] = []
|
private let clientMessagesPublisher = PassthroughSubject<(XMPPClient, Martin.Message), Never>()
|
||||||
|
private var clientStateCancellables: Set<AnyCancellable> = []
|
||||||
|
private var clientMessagesCancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
@Published private(set) var clients: [XMPPClient] = []
|
@Published private(set) var clients: [XMPPClient] = []
|
||||||
var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> {
|
var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> {
|
||||||
clientStatePublisher.eraseToAnyPublisher()
|
clientStatePublisher.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var clientMessages: AnyPublisher<(XMPPClient, Martin.Message), Never> {
|
||||||
|
clientMessagesPublisher.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
init(manager: MartinsManager) {
|
init(manager: MartinsManager) {
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
}
|
}
|
||||||
|
@ -29,14 +35,24 @@ final class XMPPService: ObservableObject {
|
||||||
|
|
||||||
// init and add clients
|
// init and add clients
|
||||||
for account in forAdd {
|
for account in forAdd {
|
||||||
|
// add client
|
||||||
let client = makeClient(for: account, with: manager)
|
let client = makeClient(for: account, with: manager)
|
||||||
clients.append(client)
|
clients.append(client)
|
||||||
let cancellable = client.$state
|
|
||||||
|
// subscribe to client state
|
||||||
|
client.$state
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
self?.clientStatePublisher.send((client, state))
|
self?.clientStatePublisher.send((client, state))
|
||||||
}
|
}
|
||||||
|
.store(in: &clientStateCancellables)
|
||||||
|
|
||||||
|
// subscribe to client messages
|
||||||
|
client.module(MessageModule.self).messagesPublisher
|
||||||
|
.sink { [weak self] message in
|
||||||
|
self?.clientMessagesPublisher.send((client, message.message))
|
||||||
|
}
|
||||||
|
.store(in: &clientMessagesCancellables)
|
||||||
|
|
||||||
clientStateCancellables.append(cancellable)
|
|
||||||
client.login()
|
client.login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ let store = AppStore(
|
||||||
XMPPMiddleware.shared.middleware,
|
XMPPMiddleware.shared.middleware,
|
||||||
RostersMiddleware.shared.middleware,
|
RostersMiddleware.shared.middleware,
|
||||||
ChatsMiddleware.shared.middleware,
|
ChatsMiddleware.shared.middleware,
|
||||||
ConversationMiddleware.shared.middleware
|
ConversationMiddleware.shared.middleware,
|
||||||
|
MessagesMiddleware.shared.middleware
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue