import Combine import Foundation final class AccountsMiddleware { static let shared = AccountsMiddleware() private lazy var allFeatures: [ServerFeature] = { guard let url = Bundle.main.url(forResource: "server_features", withExtension: "plist"), let data = try? Data(contentsOf: url), let loaded = try? PropertyListDecoder().decode([ServerFeature].self, from: data) else { return [] } return loaded }() func middleware(state: AppState, action: AppAction) -> AnyPublisher { switch action { case .databaseAction(.storedAccountsLoaded(let accounts)): return Just(.accountsAction(.accountsListUpdated(accounts: accounts))) .eraseToAnyPublisher() case .xmppAction(.clientConnectionChanged(let jid, let connectionStatus)): return Deferred { Future { promise in guard let account = state.accountsState.accounts.first(where: { $0.bareJid == jid }) else { promise(.success(.info("AccountsMiddleware: account not found for jid \(jid)"))) return } if account.isTemp { switch connectionStatus { case .connected: promise(.success(.accountsAction(.makeAccountPermanent(account: account)))) case .disconnected(let reason): if reason != "No error!" { promise(.success(.accountsAction(.addAccountError(jid: jid, reason: reason)))) } else { promise(.success(.info("AccountsMiddleware: account \(jid) disconnected with no error"))) } default: promise(.success(.info("AccountsMiddleware: account \(jid) connection status changed to \(connectionStatus)"))) } } else { promise(.success(.info("AccountsMiddleware: account \(jid) is not temporary, ignoring"))) } } } .eraseToAnyPublisher() case .xmppAction(.serverFeaturesLoaded(let jid, let features)): return Deferred { Future { [weak self] promise in let serverFeatures = features .compactMap { featureId in self?.allFeatures.first(where: { $0.xmppId == featureId }) } promise(.success(.accountsAction(.clientServerFeaturesUpdated(jid: jid, features: serverFeatures)))) } } .eraseToAnyPublisher() default: return Empty().eraseToAnyPublisher() } } }