conversations-classic-ios/ConversationsClassic/AppCore/XMPP/XMPPService.swift
2024-06-25 14:20:20 +02:00

120 lines
4.4 KiB
Swift

import Combine
import Foundation
import GRDB
import Martin
protocol MartinsManager: Martin.RosterManager & Martin.ChatManager {}
final class XMPPService: ObservableObject {
private let manager: MartinsManager
private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>()
private let clientMessagesPublisher = PassthroughSubject<(XMPPClient, Martin.Message), Never>()
private var clientStateCancellables: Set<AnyCancellable> = []
private var clientMessagesCancellables: Set<AnyCancellable> = []
@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
}
func updateClients(for accounts: [Account]) {
// get simple diff
let forAdd = accounts
.filter { !self.clients.map { $0.connectionConfiguration.userJid.stringValue }.contains($0.bareJid) }
let forRemove = clients
.map { $0.connectionConfiguration.userJid.stringValue }
.filter { !accounts.map { $0.bareJid }.contains($0) }
// init and add clients
for account in forAdd {
// add client
let client = makeClient(for: account, with: manager)
clients.append(client)
// 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)
client.login()
}
// remove clients
for jid in forRemove {
deinitClient(jid: jid)
}
}
private func makeClient(for account: Account, with manager: MartinsManager) -> XMPPClient {
let client = XMPPClient()
// register modules
// core modules RFC 6120
client.modulesManager.register(StreamFeaturesModule())
client.modulesManager.register(SaslModule())
client.modulesManager.register(AuthModule())
client.modulesManager.register(SessionEstablishmentModule())
client.modulesManager.register(ResourceBinderModule())
client.modulesManager.register(DiscoveryModule(identity: .init(category: "client", type: "iOS", name: Const.appName)))
// messaging modules RFC 6121
client.modulesManager.register(RosterModule(rosterManager: manager))
client.modulesManager.register(PresenceModule())
client.modulesManager.register(PubSubModule())
client.modulesManager.register(MessageModule(chatManager: manager))
client.modulesManager.register(MessageCarbonsModule())
client.modulesManager.register(MessageArchiveManagementModule())
// extensions
client.modulesManager.register(SoftwareVersionModule())
client.modulesManager.register(PingModule())
client.connectionConfiguration.userJid = .init(account.bareJid)
client.connectionConfiguration.credentials = .password(password: account.pass)
// add client to clients
return client
}
func deinitClient(jid: String) {
if let index = clients.firstIndex(where: { $0.connectionConfiguration.userJid.stringValue == jid }) {
let client = clients.remove(at: index)
_ = client.disconnect()
}
}
func getClient(for jid: String) -> XMPPClient? {
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
}
// TODO: add handler
func sendMessage(from: String, to: String, body: String) {
guard let client = getClient(for: from) else { return }
let message = Martin.Message()
// message.from = client.connectionConfiguration.userJid
message.to = JID(to)
message.body = body
client.writer.write(message) { res in
print(res)
}
}
}