2024-06-19 15:15:27 +00:00
|
|
|
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> = []
|
2024-07-14 19:22:46 +00:00
|
|
|
private var uploadingMessageIDs = ThreadSafeSet<String>()
|
2024-06-19 15:15:27 +00:00
|
|
|
|
|
|
|
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)
|
2024-06-24 10:44:55 +00:00
|
|
|
|
2024-06-24 13:28:26 +00:00
|
|
|
service.clientMessages.sink { _, martinMessage in
|
|
|
|
guard let message = Message.map(martinMessage) else { return }
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
store.dispatch(.xmppAction(.xmppMessageReceived(message)))
|
|
|
|
}
|
2024-06-24 10:44:55 +00:00
|
|
|
}
|
|
|
|
.store(in: &cancellables)
|
2024-07-22 12:02:33 +00:00
|
|
|
|
2024-07-22 12:18:42 +00:00
|
|
|
service.clientFeatures.sink { client, _ in
|
2024-07-22 12:02:33 +00:00
|
|
|
let jid = client.userBareJid.stringValue
|
2024-07-22 12:18:42 +00:00
|
|
|
|
|
|
|
// TODO: Fix this (BAD_ACCESS error in runtime)
|
|
|
|
// DispatchQueue.main.async {
|
|
|
|
// store.dispatch(.xmppAction(.serverFeaturesLoaded(jid: jid, features: features)))
|
|
|
|
// }
|
2024-07-22 12:02:33 +00:00
|
|
|
}
|
|
|
|
.store(in: &cancellables)
|
2024-06-19 15:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2024-07-16 14:05:15 +00:00
|
|
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
2024-06-19 15:15:27 +00:00
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
|
|
|
case .accountsAction(.addAccountError):
|
|
|
|
return Future<AppAction, Never> { [weak self] promise in
|
|
|
|
self?.service.updateClients(for: state.accountsState.accounts)
|
2024-07-16 14:05:15 +00:00
|
|
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
2024-06-19 15:15:27 +00:00
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
|
|
|
case .databaseAction(.storedAccountsLoaded(let accounts)):
|
|
|
|
return Future<AppAction, Never> { [weak self] promise in
|
|
|
|
self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp })
|
2024-07-16 14:05:15 +00:00
|
|
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
2024-06-19 15:15:27 +00:00
|
|
|
}
|
|
|
|
.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:
|
2024-07-16 14:05:15 +00:00
|
|
|
promise(.success(.info("XMPPMiddleware: roster \(contactJID) deleted from \(ownerJID)")))
|
2024-06-19 15:15:27 +00:00
|
|
|
|
|
|
|
case .failure(let error):
|
|
|
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription))))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
2024-06-26 10:26:04 +00:00
|
|
|
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()
|
2024-06-25 12:20:20 +00:00
|
|
|
|
2024-07-16 13:01:27 +00:00
|
|
|
case .xmppAction(.xmppSharingTryUpload(let message)):
|
2024-07-14 18:28:54 +00:00
|
|
|
return Future<AppAction, Never> { [weak self] promise in
|
2024-07-14 19:22:46 +00:00
|
|
|
if self?.uploadingMessageIDs.contains(message.id) ?? false {
|
2024-07-16 14:05:15 +00:00
|
|
|
return promise(.success(.info("XMPPMiddleware: attachment in message \(message.id) is already in uploading process")))
|
2024-07-14 19:22:46 +00:00
|
|
|
} else {
|
|
|
|
self?.uploadingMessageIDs.insert(message.id)
|
|
|
|
DispatchQueue.global().async {
|
|
|
|
self?.service.uploadAttachment(message: message) { error, remotePath in
|
|
|
|
self?.uploadingMessageIDs.remove(message.id)
|
|
|
|
if let error {
|
2024-07-16 13:01:27 +00:00
|
|
|
promise(.success(.xmppAction(.xmppSharingUploadFailed(msgId: message.id, reason: error.localizedDescription))))
|
2024-07-14 19:22:46 +00:00
|
|
|
} else {
|
2024-07-16 13:01:27 +00:00
|
|
|
promise(.success(.xmppAction(.xmppSharingUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
|
2024-07-14 19:22:46 +00:00
|
|
|
}
|
2024-07-14 18:28:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
2024-06-19 15:15:27 +00:00
|
|
|
default:
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|