This commit is contained in:
fmodf 2024-08-12 00:30:01 +02:00
parent eddec7412c
commit 882501782e
3 changed files with 55 additions and 46 deletions

View file

@ -16,27 +16,11 @@ enum ClientState: Equatable {
final class Client: ObservableObject { final class Client: ObservableObject {
@Published private(set) var state: ClientState = .enabled(.disconnected) @Published private(set) var state: ClientState = .enabled(.disconnected)
@Published private(set) var credentials: Credentials @Published private(set) var credentials: Credentials
@Published private(set) var rosters: [Roster] = []
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 []
}
}
}
private var connection: XMPPClient private var connection: XMPPClient
private var connectionCancellable: AnyCancellable? private var connectionCancellable: AnyCancellable?
private var rostersCancellable: AnyCancellable?
private var rosterManager = ClientMartinRosterManager() private var rosterManager = ClientMartinRosterManager()
@ -51,6 +35,18 @@ final class Client: ObservableObject {
self.state = .disabled self.state = .disabled
return 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 { switch state {
case .connected: case .connected:
self.state = .enabled(.connected) self.state = .enabled(.connected)

View file

@ -8,19 +8,20 @@ final class ClientsStore: ObservableObject {
@Published private(set) var ready = false @Published private(set) var ready = false
@Published private(set) var clients: [Client] = [] @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() { init() {
Task { credentialsCancellable = ValueObservation
do { .tracking { db in
for try await creds in credentialsObservation.values(in: Database.shared.dbQueue) { try Credentials.fetchAll(db)
processCredentials(creds)
if !ready {
ready = true
} }
} .publisher(in: Database.shared.dbQueue)
} catch {} .catch { _ in Just([]) }
.sink { [weak self] creds in
self?.processCredentials(creds)
} }
} }
@ -37,6 +38,12 @@ final class ClientsStore: ObservableObject {
var updatedClients = clients.filter { credentialsJids.contains($0.credentials.bareJid) } var updatedClients = clients.filter { credentialsJids.contains($0.credentials.bareJid) }
updatedClients.append(contentsOf: newClients) updatedClients.append(contentsOf: newClients)
clients = updatedClients clients = updatedClients
if !ready {
ready = true
}
resubscribeRosters()
} }
private func client(for credentials: Credentials) -> Client? { private func client(for credentials: Credentials) -> Client? {
@ -69,16 +76,6 @@ extension ClientsStore {
} }
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 { func addRoster(_ credentials: Credentials, contactJID: String, name: String?, groups: [String]) async throws {
// check that roster exist in db as locally deleted and undelete it // check that roster exist in db as locally deleted and undelete it
let deletedLocally = try await Roster.fetchDeletedLocally() let deletedLocally = try await Roster.fetchDeletedLocally()
@ -93,4 +90,22 @@ extension ClientsStore {
} }
try await client.addRoster(contactJID, name: name, groups: groups) 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
}
}
} }

View file

@ -4,8 +4,6 @@ struct ContactsScreen: View {
@Environment(\.router) var router @Environment(\.router) var router
@EnvironmentObject var clientsStore: ClientsStore @EnvironmentObject var clientsStore: ClientsStore
@State private var rosters: [Roster] = []
var body: some View { var body: some View {
ZStack { ZStack {
// Background color // Background color
@ -28,9 +26,9 @@ struct ContactsScreen: View {
) )
// Contacts list // Contacts list
if !rosters.isEmpty { if !clientsStore.actualRosters.isEmpty {
List { List {
ForEach(rosters) { roster in ForEach(clientsStore.actualRosters) { roster in
ContactsScreenRow( ContactsScreenRow(
roster: roster roster: roster
) )
@ -43,9 +41,9 @@ struct ContactsScreen: View {
} }
} }
} }
.task { // .task {
rosters = await clientsStore.actualRosters // rosters = await clientsStore.actualRosters
} // }
// .alert(isPresented: $isErrorAlertPresented) { // .alert(isPresented: $isErrorAlertPresented) {
// Alert( // Alert(
// title: Text(L10n.Global.Error.title), // title: Text(L10n.Global.Error.title),