91 lines
3.3 KiB
Swift
91 lines
3.3 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 var clientStateCancellables: [AnyCancellable] = []
|
||
|
|
||
|
@Published private(set) var clients: [XMPPClient] = []
|
||
|
var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> {
|
||
|
clientStatePublisher.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 {
|
||
|
let client = makeClient(for: account, with: manager)
|
||
|
clients.append(client)
|
||
|
let cancellable = client.$state
|
||
|
.sink { [weak self] state in
|
||
|
self?.clientStatePublisher.send((client, state))
|
||
|
}
|
||
|
|
||
|
clientStateCancellables.append(cancellable)
|
||
|
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 }
|
||
|
}
|
||
|
}
|