From 882501782ed88588bf801df2c6f2157904080658 Mon Sep 17 00:00:00 2001 From: fmodf Date: Mon, 12 Aug 2024 00:30:01 +0200 Subject: [PATCH] wip --- .../AppData/Client/Client.swift | 32 +++++------ .../AppData/Store/ClientsStore.swift | 57 ++++++++++++------- .../View/Main/Contacts/ContactsScreen.swift | 12 ++-- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/ConversationsClassic/AppData/Client/Client.swift b/ConversationsClassic/AppData/Client/Client.swift index 0367046..509fdad 100644 --- a/ConversationsClassic/AppData/Client/Client.swift +++ b/ConversationsClassic/AppData/Client/Client.swift @@ -16,27 +16,11 @@ enum ClientState: Equatable { final class Client: ObservableObject { @Published private(set) var state: ClientState = .enabled(.disconnected) @Published private(set) var credentials: Credentials - - var rosters: [Roster] { - get async { - // Fetching only non-locally-deleted rosters and only for active credentials - if credentials.isActive { - let creds = credentials - let rosters = try? await Database.shared.dbQueue.read { db in - try Roster - .filter(Column("bareJid") == creds.bareJid) - .filter(Column("locallyDeleted") == false) - .fetchAll(db) - } - return rosters ?? [] - } else { - return [] - } - } - } + @Published private(set) var rosters: [Roster] = [] private var connection: XMPPClient private var connectionCancellable: AnyCancellable? + private var rostersCancellable: AnyCancellable? private var rosterManager = ClientMartinRosterManager() @@ -51,6 +35,18 @@ final class Client: ObservableObject { self.state = .disabled return } + rostersCancellable = ValueObservation + .tracking { db in + try Roster + .filter(Column("bareJid") == self.credentials.bareJid) + .filter(Column("locallyDeleted") == false) + .fetchAll(db) + } + .publisher(in: Database.shared.dbQueue) + .catch { _ in Just([]) } + .sink { rosters in + self.rosters = rosters + } switch state { case .connected: self.state = .enabled(.connected) diff --git a/ConversationsClassic/AppData/Store/ClientsStore.swift b/ConversationsClassic/AppData/Store/ClientsStore.swift index 9cbe322..45cad55 100644 --- a/ConversationsClassic/AppData/Store/ClientsStore.swift +++ b/ConversationsClassic/AppData/Store/ClientsStore.swift @@ -8,20 +8,21 @@ final class ClientsStore: ObservableObject { @Published private(set) var ready = false @Published private(set) var clients: [Client] = [] + @Published private(set) var actualRosters: [Roster] = [] - private let credentialsObservation = ValueObservation.tracking(Credentials.fetchAll) + private var credentialsCancellable: AnyCancellable? + private var rostersCancellable: AnyCancellable? init() { - Task { - do { - for try await creds in credentialsObservation.values(in: Database.shared.dbQueue) { - processCredentials(creds) - if !ready { - ready = true - } - } - } catch {} - } + credentialsCancellable = ValueObservation + .tracking { db in + try Credentials.fetchAll(db) + } + .publisher(in: Database.shared.dbQueue) + .catch { _ in Just([]) } + .sink { [weak self] creds in + self?.processCredentials(creds) + } } private func processCredentials(_ credentials: [Credentials]) { @@ -37,6 +38,12 @@ final class ClientsStore: ObservableObject { var updatedClients = clients.filter { credentialsJids.contains($0.credentials.bareJid) } updatedClients.append(contentsOf: newClients) clients = updatedClients + + if !ready { + ready = true + } + + resubscribeRosters() } private func client(for credentials: Credentials) -> Client? { @@ -69,16 +76,6 @@ extension ClientsStore { } extension ClientsStore { - var actualRosters: [Roster] { - get async { - var allRosters: [Roster] = [] - for client in clients { - allRosters.append(contentsOf: await client.rosters) - } - return allRosters - } - } - func addRoster(_ credentials: Credentials, contactJID: String, name: String?, groups: [String]) async throws { // check that roster exist in db as locally deleted and undelete it let deletedLocally = try await Roster.fetchDeletedLocally() @@ -93,4 +90,22 @@ extension ClientsStore { } try await client.addRoster(contactJID, name: name, groups: groups) } + + private func resubscribeRosters() { + let clientsJids = clients + .filter { $0.state != .disabled } + .map { $0.credentials.bareJid } + + rostersCancellable = ValueObservation.tracking { db in + try Roster + .filter(clientsJids.contains(Column("bareJid"))) + .filter(Column("locallyDeleted") == false) + .fetchAll(db) + } + .publisher(in: Database.shared.dbQueue) + .catch { _ in Just([]) } + .sink { [weak self] rosters in + self?.actualRosters = rosters + } + } } diff --git a/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift b/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift index 9bd70e5..7a9feef 100644 --- a/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift +++ b/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift @@ -4,8 +4,6 @@ struct ContactsScreen: View { @Environment(\.router) var router @EnvironmentObject var clientsStore: ClientsStore - @State private var rosters: [Roster] = [] - var body: some View { ZStack { // Background color @@ -28,9 +26,9 @@ struct ContactsScreen: View { ) // Contacts list - if !rosters.isEmpty { + if !clientsStore.actualRosters.isEmpty { List { - ForEach(rosters) { roster in + ForEach(clientsStore.actualRosters) { roster in ContactsScreenRow( roster: roster ) @@ -43,9 +41,9 @@ struct ContactsScreen: View { } } } - .task { - rosters = await clientsStore.actualRosters - } + // .task { + // rosters = await clientsStore.actualRosters + // } // .alert(isPresented: $isErrorAlertPresented) { // Alert( // title: Text(L10n.Global.Error.title),