import Combine import Foundation import GRDB @MainActor final class RostersStore: ObservableObject { @Published private(set) var rosters: [Roster] = [] @Published private(set) var locallyDeletedRosters: [Roster] = [] private var cancellable: AnyCancellable? init() { let rostersPublisher = ValueObservation.tracking(Roster.fetchAll) .publisher(in: Database.shared.dbQueue) .receive(on: DispatchQueue.main) .catch { _ in Just([]) } cancellable = ClientsStore.shared.$clients .map { $0.filter { $0.state != .disabled } } // look rosters only for enabled clients .flatMap { clients in Publishers.MergeMany(clients.map { $0.$state }) .prepend(clients.map { $0.state }) .collect() } .combineLatest(rostersPublisher) .sink { [weak self] clientStates, rosters in self?.handleUpdates(clientStates: clientStates, rosters: rosters) } } private func handleUpdates(clientStates: [ClientState], rosters: [Roster]) { self.rosters = rosters.filter { !$0.locallyDeleted } locallyDeletedRosters = rosters.filter { $0.locallyDeleted } print("Client States: \(clientStates.count), Rosters: \(rosters.count)") } } extension RostersStore { enum RosterAddResult { case success case connectionError case serverError } func addRoster(_ owner: Credentials, contactJID: String, name _: String?, groups _: [String]) async -> RosterAddResult { // check if such roster was already exists or locally deleted if var exists = rosters.first(where: { $0.bareJid == owner.bareJid && $0.contactBareJid == contactJID }), exists.locallyDeleted { try? await exists.setLocallyDeleted(false) return .success } // add new roster // check that client is enabled and connected let clientStorage = ClientsStore.shared while !clientStorage.ready { await Task.yield() } print("!! Clients: \(clientStorage.clients.count)") guard let client = clientStorage.clients.first(where: { $0.credentials == owner }) else { return .connectionError } guard client.state == .enabled(.connected) else { return .connectionError } // guard let client = ClientsStore.shared.clients.first(where: { $0.credentials == owner }), client.state == .enabled(.connected) else { // return .connectionError // } // add roster do { try await client.addRoster(contactJID, name: nil, groups: []) return .success } catch { return .serverError } // guard let client = clientsStore.clients.first(where: { $0.credentials == ownerCredentials }), client.state == .enabled(.connected) else { // router.showAlert( // .alert, // title: L10n.Global.Error.title, // subtitle: L10n.Contacts.Add.connectionError // ) { // Button(L10n.Global.ok, role: .cancel) {} // } // return // } // // router.showModal { // LoadingScreen() // } // // do { // try await client.addRoster(contactJID, name: nil, groups: []) // } catch { // router.showAlert( // .alert, // title: L10n.Global.Error.title, // subtitle: L10n.Contacts.Add.serverError // ) { // Button(L10n.Global.ok, role: .cancel) {} // } // return // } // // router.dismissModal() // router.dismissScreen() // client.addRoster(jid: ) // guard let ownerAccount else { return } // if let exists = store.state.rostersState.rosters.first(where: { $0.bareJid == ownerAccount.bareJid && $0.contactBareJid == contactJID }), exists.locallyDeleted { // store.dispatch(.rostersAction(.unmarkRosterAsLocallyDeleted(ownerJID: ownerAccount.bareJid, contactJID: contactJID))) // isPresented = false // } else { // isShowingLoader = true // store.dispatch(.rostersAction(.addRoster(ownerJID: ownerAccount.bareJid, contactJID: contactJID, name: nil, groups: []))) // } } }