import Combine import Foundation import GRDB @MainActor final class ClientsStore: ObservableObject { static let shared = ClientsStore() @Published private(set) var ready = false @Published private(set) var clients: [Client] = [] private let observation = ValueObservation.tracking(Credentials.fetchAll) func startFetching() { Task { do { for try await credentials in observation.values(in: Database.shared.dbQueue) { processCredentials(credentials) ready = true print("Fetched \(credentials.count) credentials") } } catch {} } } private func processCredentials(_ credentials: [Credentials]) { let existsJids = Set(clients.map { $0.credentials.bareJid }) let credentialsJids = Set(credentials.map { $0.bareJid }) let forAdd = credentials.filter { !existsJids.contains($0.bareJid) } let newClients = forAdd.map { Client(credentials: $0) } let forRemove = clients.filter { !credentialsJids.contains($0.credentials.bareJid) } forRemove.forEach { $0.disconnect() } var updatedClients = clients.filter { credentialsJids.contains($0.credentials.bareJid) } updatedClients.append(contentsOf: newClients) clients = updatedClients } } extension ClientsStore { func addNewClient(_ client: Client) { clients.append(client) Task(priority: .background) { try? await client.credentials.save() } } func reconnectAll() { Task { await withTaskGroup(of: Void.self) { taskGroup in for client in clients { taskGroup.addTask { await client.connect() } } } } } }