wip
This commit is contained in:
parent
da104ec43e
commit
e8d39f6ab6
|
@ -0,0 +1,47 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
final class ClientMartinCarbonsManager {
|
||||||
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
init(_ xmppConnection: XMPPClient) {
|
||||||
|
// subscribe to carbons
|
||||||
|
xmppConnection.module(MessageCarbonsModule.self).carbonsPublisher
|
||||||
|
.sink { [weak self] carbon in
|
||||||
|
self?.handleMessage(carbon)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
// enable carbons if available
|
||||||
|
xmppConnection.module(.messageCarbons).$isAvailable.filter { $0 }
|
||||||
|
.sink(receiveValue: { [weak xmppConnection] _ in
|
||||||
|
xmppConnection?.module(.messageCarbons).enable()
|
||||||
|
})
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleMessage(_ received: Martin.MessageCarbonsModule.CarbonReceived) {
|
||||||
|
let message = received.message
|
||||||
|
let action = received.action
|
||||||
|
let onJid = received.jid
|
||||||
|
#if DEBUG
|
||||||
|
print("---")
|
||||||
|
print("Carbons message received: \(message)")
|
||||||
|
print("Action: \(action)")
|
||||||
|
print("On JID: \(onJid)")
|
||||||
|
print("---")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if let msg = Message.map(message) {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await msg.save()
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error saving message: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
ConversationsClassic/AppData/Client/Client+MartinMAM.swift
Normal file
42
ConversationsClassic/AppData/Client/Client+MartinMAM.swift
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
final class ClientMartinMAM {
|
||||||
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
init(_ xmppConnection: XMPPClient) {
|
||||||
|
// subscribe to archived messages
|
||||||
|
xmppConnection.module(.mam).archivedMessagesPublisher
|
||||||
|
.sink(receiveValue: { [weak self] archived in
|
||||||
|
let message = archived.message
|
||||||
|
message.attribute("archived_date", newValue: "\(archived.timestamp.timeIntervalSince1970)")
|
||||||
|
self?.handleMessage(archived)
|
||||||
|
})
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleMessage(_ received: Martin.MessageArchiveManagementModule.ArchivedMessageReceived) {
|
||||||
|
let message = received.message
|
||||||
|
let date = received.timestamp
|
||||||
|
#if DEBUG
|
||||||
|
print("---")
|
||||||
|
print("Archive message received: \(message)")
|
||||||
|
print("Date: \(date)")
|
||||||
|
print("---")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if let msg = Message.map(message) {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
var msg = msg
|
||||||
|
msg.date = received.timestamp
|
||||||
|
try await msg.save()
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error saving message: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,97 +7,24 @@ final class ClientMartinMessagesManager {
|
||||||
private var cancellables: Set<AnyCancellable> = []
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
init(_ xmppConnection: XMPPClient) {
|
init(_ xmppConnection: XMPPClient) {
|
||||||
// subscribe to client messages
|
|
||||||
xmppConnection.module(MessageModule.self).messagesPublisher
|
xmppConnection.module(MessageModule.self).messagesPublisher
|
||||||
.sink { [weak self] message in
|
.sink { [weak self] message in
|
||||||
self?.handleMessage(message.message)
|
|
||||||
}
|
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
// subscribe to carbons
|
|
||||||
xmppConnection.module(MessageCarbonsModule.self).carbonsPublisher
|
|
||||||
.sink { [weak self] carbon in
|
|
||||||
self?.handleMessage(carbon.message)
|
|
||||||
}
|
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
// subscribe to archived messages
|
|
||||||
xmppConnection.module(.mam).archivedMessagesPublisher
|
|
||||||
.sink(receiveValue: { [weak self] archived in
|
|
||||||
let message = archived.message
|
|
||||||
message.attribute("archived_date", newValue: "\(archived.timestamp.timeIntervalSince1970)")
|
|
||||||
self?.handleMessage(message)
|
self?.handleMessage(message)
|
||||||
})
|
}
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
// enable carbons if available
|
|
||||||
xmppConnection.module(.messageCarbons).$isAvailable.filter { $0 }
|
|
||||||
.sink(receiveValue: { [weak xmppConnection] _ in
|
|
||||||
xmppConnection?.module(.messageCarbons).enable()
|
|
||||||
})
|
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleMessage(_ received: Martin.Message) {
|
private func handleMessage(_ received: Martin.MessageModule.MessageReceived) {
|
||||||
|
let message = received.message
|
||||||
|
let chat = received.chat
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
print("---")
|
print("---")
|
||||||
print("Message received: \(received)")
|
print("Message received: \(received)")
|
||||||
|
print("Chat: \(chat)")
|
||||||
print("---")
|
print("---")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check that the message type is supported
|
if let msg = Message.map(message) {
|
||||||
let chatTypes: [StanzaType] = [.chat, .groupchat]
|
|
||||||
guard let mType = received.type, chatTypes.contains(mType) else {
|
|
||||||
#if DEBUG
|
|
||||||
print("Unsupported received type: \(received.type?.rawValue ?? "nil")")
|
|
||||||
#endif
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type
|
|
||||||
let type = MessageType(rawValue: received.type?.rawValue ?? "") ?? .chat
|
|
||||||
|
|
||||||
// Content type
|
|
||||||
var contentType: MessageContentType = .text
|
|
||||||
if let oob = received.oob {
|
|
||||||
contentType = .attachment(.init(
|
|
||||||
type: oob.attachmentType,
|
|
||||||
localName: nil,
|
|
||||||
thumbnailName: nil,
|
|
||||||
remotePath: oob
|
|
||||||
))
|
|
||||||
} else if received.hints.contains(.noStore) {
|
|
||||||
contentType = .typing
|
|
||||||
// skip for now
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// From/To
|
|
||||||
let from = received.from?.bareJid.stringValue ?? ""
|
|
||||||
let to = received.to?.bareJid.stringValue
|
|
||||||
|
|
||||||
// Extract date or set current
|
|
||||||
var date = Date()
|
|
||||||
if let timestampStr = received.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) {
|
|
||||||
date = Date(timeIntervalSince1970: timeInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Msg
|
|
||||||
let msg = Message(
|
|
||||||
id: received.id ?? UUID().uuidString,
|
|
||||||
type: type,
|
|
||||||
date: date,
|
|
||||||
contentType: contentType,
|
|
||||||
status: .sent,
|
|
||||||
from: from,
|
|
||||||
to: to,
|
|
||||||
body: received.body,
|
|
||||||
subject: received.subject,
|
|
||||||
thread: received.thread,
|
|
||||||
oobUrl: received.oob
|
|
||||||
)
|
|
||||||
|
|
||||||
// Save message
|
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await msg.save()
|
try await msg.save()
|
||||||
|
@ -107,3 +34,4 @@ final class ClientMartinMessagesManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,15 +24,19 @@ final class Client: ObservableObject {
|
||||||
|
|
||||||
private var rosterManager = ClientMartinRosterManager()
|
private var rosterManager = ClientMartinRosterManager()
|
||||||
private var chatsManager = ClientMartinChatsManager()
|
private var chatsManager = ClientMartinChatsManager()
|
||||||
private var messageManager: ClientMartinMessagesManager
|
|
||||||
private var discoManager: ClientMartinDiscoManager
|
private var discoManager: ClientMartinDiscoManager
|
||||||
|
private var messageManager: ClientMartinMessagesManager
|
||||||
|
private var carbonsManager: ClientMartinCarbonsManager
|
||||||
|
private var mamManager: ClientMartinMAM
|
||||||
|
|
||||||
init(credentials: Credentials) {
|
init(credentials: Credentials) {
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
state = credentials.isActive ? .enabled(.disconnected) : .disabled
|
state = credentials.isActive ? .enabled(.disconnected) : .disabled
|
||||||
connection = Self.prepareConnection(credentials, rosterManager, chatsManager)
|
connection = Self.prepareConnection(credentials, rosterManager, chatsManager)
|
||||||
messageManager = ClientMartinMessagesManager(connection)
|
|
||||||
discoManager = ClientMartinDiscoManager(connection)
|
discoManager = ClientMartinDiscoManager(connection)
|
||||||
|
messageManager = ClientMartinMessagesManager(connection)
|
||||||
|
carbonsManager = ClientMartinCarbonsManager(connection)
|
||||||
|
mamManager = ClientMartinMAM(connection)
|
||||||
connectionCancellable = connection.$state
|
connectionCancellable = connection.$state
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct Message: DBStorable, Equatable {
|
||||||
|
|
||||||
let id: String
|
let id: String
|
||||||
var type: MessageType
|
var type: MessageType
|
||||||
let date: Date
|
var date: Date
|
||||||
var contentType: MessageContentType
|
var contentType: MessageContentType
|
||||||
var status: MessageStatus
|
var status: MessageStatus
|
||||||
|
|
||||||
|
@ -101,3 +101,54 @@ extension Message {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Message {
|
||||||
|
static func map(_ martinMessage: Martin.Message) -> Message? {
|
||||||
|
// 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 martinMessage type: \(martinMessage.type?.rawValue ?? "nil")")
|
||||||
|
#endif
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat
|
||||||
|
|
||||||
|
// Content type
|
||||||
|
var contentType: MessageContentType = .text
|
||||||
|
if let oob = martinMessage.oob {
|
||||||
|
contentType = .attachment(.init(
|
||||||
|
type: oob.attachmentType,
|
||||||
|
localName: nil,
|
||||||
|
thumbnailName: nil,
|
||||||
|
remotePath: oob
|
||||||
|
))
|
||||||
|
} else if martinMessage.hints.contains(.noStore) {
|
||||||
|
contentType = .typing
|
||||||
|
// skip for now
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// From/To
|
||||||
|
let from = martinMessage.from?.bareJid.stringValue ?? ""
|
||||||
|
let to = martinMessage.to?.bareJid.stringValue
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ private extension AttachmentsStore {
|
||||||
.sink { _ in
|
.sink { _ in
|
||||||
} receiveValue: { [weak self] messages in
|
} receiveValue: { [weak self] messages in
|
||||||
let forProcessing = messages
|
let forProcessing = messages
|
||||||
// .filter { $0.status == .pending }
|
.filter { $0.status != .error }
|
||||||
.filter { self?.processing.contains($0.id) == false }
|
.filter { self?.processing.contains($0.id) == false }
|
||||||
.filter { $0.contentType.isAttachment }
|
.filter { $0.contentType.isAttachment }
|
||||||
for message in forProcessing {
|
for message in forProcessing {
|
||||||
|
|
Loading…
Reference in a new issue