From 49dd4b158c2b37e2b51b737d8660aa40b5ccca40 Mon Sep 17 00:00:00 2001 From: fmodf Date: Wed, 14 Aug 2024 15:48:30 +0200 Subject: [PATCH] wip --- .../Client/Client+MartinMessages.swift | 115 +++++++++--------- .../AppData/Model/Message.swift | 8 ++ .../AppData/Store/ConversationStore.swift | 48 +++++++- ConversationsClassic/Helpers/Const.swift | 4 +- project.yml | 10 ++ 5 files changed, 123 insertions(+), 62 deletions(-) diff --git a/ConversationsClassic/AppData/Client/Client+MartinMessages.swift b/ConversationsClassic/AppData/Client/Client+MartinMessages.swift index effaf1d..9a3359f 100644 --- a/ConversationsClassic/AppData/Client/Client+MartinMessages.swift +++ b/ConversationsClassic/AppData/Client/Client+MartinMessages.swift @@ -1,4 +1,5 @@ import Combine +import Foundation import GRDB import Martin @@ -12,66 +13,66 @@ final class ClientMartinMessagesManager { } } - private func handleClientMessage(_ martinMessage: MessageModule.MessageReceived) { + private func handleClientMessage(_ received: MessageModule.MessageReceived) { + let message = received.message + let chat = received.chat #if DEBUG print("---") - print("Message received: \(martinMessage)") + print("Message received: \(message)") + print("In chat: \(chat)") print("---") #endif + + // Check that the message type is supported + let chatTypes: [StanzaType] = [.chat, .groupchat] + guard let mType = message.type, chatTypes.contains(mType) else { + #if DEBUG + print("Unsupported message type: \(message.type?.rawValue ?? "nil")") + #endif + return + } + + // Type + let type = MessageType(rawValue: message.type?.rawValue ?? "") ?? .chat + + // Content type + var contentType: MessageContentType = .text + if message.hints.contains(.noStore) { + contentType = .typing + } + + // From/To + let from = message.from?.bareJid.stringValue ?? "" + let to = message.to?.bareJid.stringValue + + // Extract date or set current + var date = Date() + if let timestampStr = message.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) { + date = Date(timeIntervalSince1970: timeInterval) + } + + // Msg + let msg = Message( + id: message.id ?? UUID().uuidString, + type: type, + date: date, + contentType: contentType, + status: .sent, + from: from, + to: to, + body: message.body, + subject: message.subject, + thread: message.thread, + oobUrl: message.oob + ) + + // Save message + Task { + do { + try await msg.save() + } catch { + logIt(.error, "Error saving message: \(error)") + } + } } } - -// -// extension Message { -// static func map(from martinMessage: Martin.Message) -> Message? { -// #if DEBUG -// print("---") -// print("Message received: \(martinMessage)") -// print("---") -// #endif -// -// // Check that the message type is supported -// let chatTypes: [StanzaType] = [.chat, .groupchat] -// guard let mType = martinMessage.type, chatTypes.contains(mType) else { -// #if DEBUG -// print("Unsupported message type: \(martinMessage.type?.rawValue ?? "nil")") -// #endif -// return nil -// } -// -// // Type -// let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat -// -// // Content type -// var contentType: MessageContentType = .text -// if martinMessage.hints.contains(.noStore) { -// contentType = .typing -// } -// -// // From/To -// let from = martinMessage.from?.bareJid.stringValue ?? "" -// let to = martinMessage.to?.bareJid.stringValue -// -// // Extract date or set current -// var date = Date() -// if let timestampStr = martinMessage.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) { -// date = Date(timeIntervalSince1970: timeInterval) -// } -// -// // Msg -// let msg = Message( -// id: martinMessage.id ?? UUID().uuidString, -// type: type, -// date: date, -// contentType: contentType, -// status: .sent, -// from: from, -// to: to, -// body: martinMessage.body, -// subject: martinMessage.subject, -// thread: martinMessage.thread, -// oobUrl: martinMessage.oob -// ) -// return msg -// } -// } diff --git a/ConversationsClassic/AppData/Model/Message.swift b/ConversationsClassic/AppData/Model/Message.swift index 8d4dcfd..a9a4a26 100644 --- a/ConversationsClassic/AppData/Model/Message.swift +++ b/ConversationsClassic/AppData/Model/Message.swift @@ -36,3 +36,11 @@ struct Message: DBStorable, Equatable { let thread: String? let oobUrl: String? } + +extension Message { + func save() async throws { + try await Database.shared.dbQueue.write { db in + try self.insert(db) + } + } +} diff --git a/ConversationsClassic/AppData/Store/ConversationStore.swift b/ConversationsClassic/AppData/Store/ConversationStore.swift index 5b2a461..fac55ad 100644 --- a/ConversationsClassic/AppData/Store/ConversationStore.swift +++ b/ConversationsClassic/AppData/Store/ConversationStore.swift @@ -1,15 +1,57 @@ +import Collections +import Combine import Foundation +import GRDB @MainActor final class ConversationStore: ObservableObject { - @Published private(set) var roster: Roster - @Published private(set) var messages: [String] = [] - @Published private(set) var test = true + @Published private(set) var messages: Deque = [] + private(set) var roster: Roster private let client: Client + private let blockSize = Const.messagesPageSize + private let messagesMax = Const.messagesMaxSize + + private var messagesObservation: AnyDatabaseCancellable init(roster: Roster, client: Client) { self.client = client self.roster = roster + + // observe change messages in database + messagesObservation = DatabaseRegionObservation(tracking: Message.all()).start(in: Database.shared.dbQueue) { _ in + // Handle error + } onChange: { _ in + print("Messages were changed") + } + } +} + +extension ConversationStore { + func loadMoreBackward() async { + // guard let lastMessage = messages.last else { return } + // let messages = await fetchBlock(lastMessage.date, nil) + // self.messages.append(contentsOf: messages) + } + + func loadMoreForward() async { + // guard let firstMessage = messages.first else { return } + // let messages = await fetchBlock(nil, firstMessage.date) + // self.messages.insert(contentsOf: messages, at: 0) + } +} + +extension ConversationStore { + private func fetchBlock(_ beforeDate: Date?, _ afterDate: Date?) async -> [Message] { + print(beforeDate, afterDate) + return [] + // let messages = await client.fetchMessages() + // self.messages.append(contentsOf: messages) + } + + private func cutMessages() { + // if messages.count > messagesMax { + // messages.removeFirst(messages.count - messagesMax) + // } } } diff --git a/ConversationsClassic/Helpers/Const.swift b/ConversationsClassic/Helpers/Const.swift index c96edfd..8a9eb50 100644 --- a/ConversationsClassic/Helpers/Const.swift +++ b/ConversationsClassic/Helpers/Const.swift @@ -48,6 +48,6 @@ enum Const { static let mamRequestDaysLength = 30 // Limits for messages pagination - static let messagesPageMin = 20 - static let messagesPageMax = 100 + static let messagesPageSize = 20 // size for block requesting + static let messagesMaxSize = 100 // total messages in memory } diff --git a/project.yml b/project.yml index a07200b..e4f5f68 100644 --- a/project.yml +++ b/project.yml @@ -5,6 +5,12 @@ options: postGenCommand: swiftgen packages: + Collections: + url: https://github.com/apple/swift-collections + majorVersion: 1.1.2 + Algorithms: + url: https://github.com/apple/swift-algorithms + majorVersion: 1.2.0 SwiftfulRouting: url: https://github.com/SwiftfulThinking/SwiftfulRouting majorVersion: 5.3.5 @@ -77,6 +83,10 @@ targets: - sdk: Security.framework # - framework: Lib/WebRTC.xcframework # - target: Engine + - package: Collections + link: true + - package: Algorithms + link: true - package: SwiftfulRouting link: true - package: MartinOMEMO