another.im-ios/ConversationsClassic/AppCore/Middlewares/XMPPMiddleware.swift
2024-07-14 20:28:54 +02:00

122 lines
5.4 KiB
Swift

import Combine
import Foundation
import Martin
final class XMPPMiddleware {
static let shared = XMPPMiddleware()
private let service = XMPPService(manager: Database.shared)
private var cancellables: Set<AnyCancellable> = []
private init() {
service.clientState.sink { client, state in
let jid = client.userBareJid.stringValue
let status = ConnectionStatus.from(state)
let action = AppAction.xmppAction(.clientConnectionChanged(jid: jid, state: status))
DispatchQueue.main.async {
store.dispatch(action)
}
}
.store(in: &cancellables)
service.clientMessages.sink { _, martinMessage in
guard let message = Message.map(martinMessage) else { return }
DispatchQueue.main.async {
store.dispatch(.xmppAction(.xmppMessageReceived(message)))
}
}
.store(in: &cancellables)
}
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
switch action {
case .accountsAction(.tryAddAccountWithCredentials):
return Future<AppAction, Never> { [weak self] promise in
self?.service.updateClients(for: state.accountsState.accounts)
promise(.success(.empty))
}
.eraseToAnyPublisher()
case .accountsAction(.addAccountError):
return Future<AppAction, Never> { [weak self] promise in
self?.service.updateClients(for: state.accountsState.accounts)
promise(.success(.empty))
}
.eraseToAnyPublisher()
case .databaseAction(.storedAccountsLoaded(let accounts)):
return Future<AppAction, Never> { [weak self] promise in
self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp })
promise(.success(.empty))
}
.eraseToAnyPublisher()
case .rostersAction(.addRoster(let ownerJID, let contactJID, let name, let groups)):
return Future<AppAction, Never> { [weak self] promise in
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
return promise(.success(.rostersAction(.addRosterError(reason: XMPPError.item_not_found.localizedDescription))))
}
let module = client.modulesManager.module(RosterModule.self)
module.addItem(jid: JID(contactJID), name: name, groups: groups, completionHandler: { result in
switch result {
case .success:
promise(.success(.rostersAction(.addRosterDone(jid: contactJID))))
case .failure(let error):
promise(.success(.rostersAction(.addRosterError(reason: error.localizedDescription))))
}
})
}
.eraseToAnyPublisher()
case .rostersAction(.deleteRoster(let ownerJID, let contactJID)):
return Future<AppAction, Never> { [weak self] promise in
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
return promise(.success(.rostersAction(.rosterDeletingFailed(reason: XMPPError.item_not_found.localizedDescription))))
}
let module = client.modulesManager.module(RosterModule.self)
module.removeItem(jid: JID(contactJID), completionHandler: { result in
switch result {
case .success:
promise(.success(.empty))
case .failure(let error):
promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription))))
}
})
}
.eraseToAnyPublisher()
case .xmppAction(.xmppMessageSent(let message)):
return Future<AppAction, Never> { [weak self] promise in
DispatchQueue.global().async {
self?.service.sendMessage(message: message) { done in
if done {
promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id))))
} else {
promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id))))
}
}
}
}
.eraseToAnyPublisher()
case .xmppAction(.xmppAttachmentUpload(let message)):
return Future<AppAction, Never> { [weak self] promise in
DispatchQueue.global().async {
self?.service.uploadAttachment(message: message) { done, remotePath in
if done {
promise(.success(.xmppAction(.xmppAttachmentUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
} else {
promise(.success(.xmppAction(.xmppAttachmentUploadFailed(msgId: message.id, reason: "Upload failed"))))
}
}
}
}
.eraseToAnyPublisher()
default:
return Empty().eraseToAnyPublisher()
}
}
}