From 8ce21712b7e1a520222b3175aca902144112597e Mon Sep 17 00:00:00 2001 From: fmodf Date: Mon, 24 Jun 2024 12:44:55 +0200 Subject: [PATCH] wip --- .../AppCore/Actions/AppActions.swift | 1 + .../AppCore/Actions/MessagesActions.swift | 4 + .../Database/Database+Migrations.swift | 1 + .../Middlewares/MessagesMiddleware.swift | 12 +++ .../AppCore/Middlewares/XMPPMiddleware.swift | 20 ++++ .../AppCore/Models/Message.swift | 100 +++++++++++++++--- .../AppCore/Reducers/AppReducer.swift | 3 + .../AppCore/State/ConversationState.swift | 4 +- .../AppCore/XMPP/XMPPService.swift | 22 +++- .../ConversationsClassicApp.swift | 3 +- 10 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 ConversationsClassic/AppCore/Actions/MessagesActions.swift create mode 100644 ConversationsClassic/AppCore/Middlewares/MessagesMiddleware.swift diff --git a/ConversationsClassic/AppCore/Actions/AppActions.swift b/ConversationsClassic/AppCore/Actions/AppActions.swift index 97f387b..4011895 100644 --- a/ConversationsClassic/AppCore/Actions/AppActions.swift +++ b/ConversationsClassic/AppCore/Actions/AppActions.swift @@ -10,4 +10,5 @@ enum AppAction: Codable { case rostersAction(_ action: RostersAction) case chatsAction(_ action: ChatsAction) case conversationAction(_ action: ConversationAction) + case messagesAction(_ action: MessagesAction) } diff --git a/ConversationsClassic/AppCore/Actions/MessagesActions.swift b/ConversationsClassic/AppCore/Actions/MessagesActions.swift new file mode 100644 index 0000000..8fb67d2 --- /dev/null +++ b/ConversationsClassic/AppCore/Actions/MessagesActions.swift @@ -0,0 +1,4 @@ +enum MessagesAction: Codable { + case newMessageReceived(Message) + case messageDraftUpdate(Message) +} diff --git a/ConversationsClassic/AppCore/Database/Database+Migrations.swift b/ConversationsClassic/AppCore/Database/Database+Migrations.swift index 522dc64..db3f446 100644 --- a/ConversationsClassic/AppCore/Database/Database+Migrations.swift +++ b/ConversationsClassic/AppCore/Database/Database+Migrations.swift @@ -52,6 +52,7 @@ extension Database { table.column("toJid", .text).notNull() table.column("timestamp", .datetime).notNull() table.column("body", .text) + table.column("type", .text).notNull() // table.column("isReaded", .boolean).notNull().defaults(to: false) // table.column("subject", .text) // table.column("threadId", .text) diff --git a/ConversationsClassic/AppCore/Middlewares/MessagesMiddleware.swift b/ConversationsClassic/AppCore/Middlewares/MessagesMiddleware.swift new file mode 100644 index 0000000..d7042d9 --- /dev/null +++ b/ConversationsClassic/AppCore/Middlewares/MessagesMiddleware.swift @@ -0,0 +1,12 @@ +import Combine + +final class MessagesMiddleware { + static let shared = MessagesMiddleware() + + func middleware(state _: AppState, action: AppAction) -> AnyPublisher { + switch action { + default: + return Empty().eraseToAnyPublisher() + } + } +} diff --git a/ConversationsClassic/AppCore/Middlewares/XMPPMiddleware.swift b/ConversationsClassic/AppCore/Middlewares/XMPPMiddleware.swift index 0b48440..7c2e5b2 100644 --- a/ConversationsClassic/AppCore/Middlewares/XMPPMiddleware.swift +++ b/ConversationsClassic/AppCore/Middlewares/XMPPMiddleware.swift @@ -17,6 +17,26 @@ final class XMPPMiddleware { } } .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 { diff --git a/ConversationsClassic/AppCore/Models/Message.swift b/ConversationsClassic/AppCore/Models/Message.swift index cd06541..4077c00 100644 --- a/ConversationsClassic/AppCore/Models/Message.swift +++ b/ConversationsClassic/AppCore/Models/Message.swift @@ -1,30 +1,98 @@ import Foundation import GRDB +import Martin enum MessageType: String, Codable, DatabaseValueConvertible { + case chat + case channel + case groupchat +} + +enum MessageContentType: String, Codable, DatabaseValueConvertible { case text case image case video case audio case file case location + case typing + case invite } -struct Message: DBStorable, Equatable { - static let databaseTableName = "messages" - +struct MessageContainer: Stateable, DatabaseValueConvertible { let id: String - let chatId: String - let fromJid: String - 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 - } + let type: MessageType + let content: any MessageContent } + +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 +// // ) +// } +// } diff --git a/ConversationsClassic/AppCore/Reducers/AppReducer.swift b/ConversationsClassic/AppCore/Reducers/AppReducer.swift index 5c8ebf2..3ad19d1 100644 --- a/ConversationsClassic/AppCore/Reducers/AppReducer.swift +++ b/ConversationsClassic/AppCore/Reducers/AppReducer.swift @@ -27,6 +27,9 @@ extension AppState { case .conversationAction(let action): ConversationState.reducer(state: &state.conversationsState, action: action) + + case .messagesAction: + break // messages actions are processed by MessagesMiddleware, and other components } } } diff --git a/ConversationsClassic/AppCore/State/ConversationState.swift b/ConversationsClassic/AppCore/State/ConversationState.swift index b651573..39a9810 100644 --- a/ConversationsClassic/AppCore/State/ConversationState.swift +++ b/ConversationsClassic/AppCore/State/ConversationState.swift @@ -1,11 +1,11 @@ struct ConversationState: Stateable { var currentChat: Chat? - var dumb: Bool + var currentMessages: [Message] } // MARK: Init extension ConversationState { init() { - dumb = false + currentMessages = [] } } diff --git a/ConversationsClassic/AppCore/XMPP/XMPPService.swift b/ConversationsClassic/AppCore/XMPP/XMPPService.swift index c6b6ddd..ecd6f9c 100644 --- a/ConversationsClassic/AppCore/XMPP/XMPPService.swift +++ b/ConversationsClassic/AppCore/XMPP/XMPPService.swift @@ -8,13 +8,19 @@ protocol MartinsManager: Martin.RosterManager & Martin.ChatManager {} final class XMPPService: ObservableObject { private let manager: MartinsManager private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>() - private var clientStateCancellables: [AnyCancellable] = [] + private let clientMessagesPublisher = PassthroughSubject<(XMPPClient, Martin.Message), Never>() + private var clientStateCancellables: Set = [] + private var clientMessagesCancellables: Set = [] @Published private(set) var clients: [XMPPClient] = [] var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> { clientStatePublisher.eraseToAnyPublisher() } + var clientMessages: AnyPublisher<(XMPPClient, Martin.Message), Never> { + clientMessagesPublisher.eraseToAnyPublisher() + } + init(manager: MartinsManager) { self.manager = manager } @@ -29,14 +35,24 @@ final class XMPPService: ObservableObject { // init and add clients for account in forAdd { + // add client let client = makeClient(for: account, with: manager) clients.append(client) - let cancellable = client.$state + + // subscribe to client state + client.$state .sink { [weak self] state in 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() } diff --git a/ConversationsClassic/ConversationsClassicApp.swift b/ConversationsClassic/ConversationsClassicApp.swift index aa485fd..41861a3 100644 --- a/ConversationsClassic/ConversationsClassicApp.swift +++ b/ConversationsClassic/ConversationsClassicApp.swift @@ -13,7 +13,8 @@ let store = AppStore( XMPPMiddleware.shared.middleware, RostersMiddleware.shared.middleware, ChatsMiddleware.shared.middleware, - ConversationMiddleware.shared.middleware + ConversationMiddleware.shared.middleware, + MessagesMiddleware.shared.middleware ] )