mv-experiment #1
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue