This commit is contained in:
fmodf 2024-08-11 21:01:48 +02:00
parent a93b9d47c2
commit c9c1d078fc
5 changed files with 60 additions and 20 deletions

View file

@ -41,7 +41,9 @@ final class Client: ObservableObject {
} }
} }
} }
}
extension Client {
func addRoster(_ jid: String, name: String?, groups: [String]) async throws { func addRoster(_ jid: String, name: String?, groups: [String]) async throws {
_ = try await connection.module(.roster).addItem( _ = try await connection.module(.roster).addItem(
jid: JID(jid), jid: JID(jid),
@ -53,6 +55,17 @@ final class Client: ObservableObject {
func deleteRoster(_ roster: Roster) async throws { func deleteRoster(_ roster: Roster) async throws {
_ = try await connection.module(.roster).removeItem(jid: JID(roster.contactBareJid)) _ = try await connection.module(.roster).removeItem(jid: JID(roster.contactBareJid))
} }
func connect() async {
guard credentials.isActive, state == .enabled(.disconnected) else {
return
}
try? await connection.loginAndWait()
}
func disconnect() {
_ = connection.disconnect()
}
} }
extension Client { extension Client {

View file

@ -9,9 +9,10 @@ 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] = []
private let observation = ValueObservation.tracking(Credentials.fetchAll)
func startFetching() { func startFetching() {
Task { Task {
let observation = ValueObservation.tracking(Credentials.fetchAll)
do { do {
for try await credentials in observation.values(in: Database.shared.dbQueue) { for try await credentials in observation.values(in: Database.shared.dbQueue) {
processCredentials(credentials) processCredentials(credentials)
@ -22,6 +23,23 @@ final class ClientsStore: ObservableObject {
} }
} }
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) { func addNewClient(_ client: Client) {
clients.append(client) clients.append(client)
Task(priority: .background) { Task(priority: .background) {
@ -29,13 +47,15 @@ final class ClientsStore: ObservableObject {
} }
} }
private func processCredentials(_ credentials: [Credentials]) { func reconnectAll() {
let existsJids = clients.map { $0.credentials.bareJid } Task {
let forAdd = credentials.filter { !existsJids.contains($0.bareJid) } await withTaskGroup(of: Void.self) { taskGroup in
let forRemove = existsJids.filter { !credentials.map { $0.bareJid }.contains($0) } for client in clients {
taskGroup.addTask {
var newClients = clients.filter { !forRemove.contains($0.credentials.bareJid) } await client.connect()
newClients.append(contentsOf: forAdd.map { Client(credentials: $0) }) }
clients = newClients }
}
}
} }
} }

View file

@ -9,17 +9,13 @@ final class RostersStore: ObservableObject {
private var cancellable: AnyCancellable? private var cancellable: AnyCancellable?
init(clientsPublisher: Published<[Client]>.Publisher) { init() {
subscribeToClientsStore(clientsPublisher: clientsPublisher)
}
private func subscribeToClientsStore(clientsPublisher: Published<[Client]>.Publisher) {
let rostersPublisher = ValueObservation.tracking(Roster.fetchAll) let rostersPublisher = ValueObservation.tracking(Roster.fetchAll)
.publisher(in: Database.shared.dbQueue) .publisher(in: Database.shared.dbQueue)
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.catch { _ in Just([]) } .catch { _ in Just([]) }
cancellable = clientsPublisher cancellable = ClientsStore.shared.$clients
.map { $0.filter { $0.state != .disabled } } // look rosters only for enabled clients .map { $0.filter { $0.state != .disabled } } // look rosters only for enabled clients
.flatMap { clients in .flatMap { clients in
Publishers.MergeMany(clients.map { $0.$state }) Publishers.MergeMany(clients.map { $0.$state })
@ -46,18 +42,29 @@ extension RostersStore {
case serverError case serverError
} }
func addRoster(ownerJid: String, contactJID: String, name _: String?, groups _: [String]) async -> RosterAddResult { func addRoster(_ owner: Credentials, contactJID: String, name _: String?, groups _: [String]) async -> RosterAddResult {
// check if such roster was already exists or locally deleted // check if such roster was already exists or locally deleted
if var exists = rosters.first(where: { $0.bareJid == ownerJid && $0.contactBareJid == contactJID }), exists.locallyDeleted { if var exists = rosters.first(where: { $0.bareJid == owner.bareJid && $0.contactBareJid == contactJID }), exists.locallyDeleted {
try? await exists.setLocallyDeleted(false) try? await exists.setLocallyDeleted(false)
return .success return .success
} }
// add new roster // add new roster
// check that client is enabled and connected // check that client is enabled and connected
guard let client = ClientsStore.shared.clients.first(where: { $0.credentials.bareJid == ownerJid }), client.state == .enabled(.connected) else { 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 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 // add roster
do { do {

View file

@ -130,7 +130,7 @@ struct AddContactOrChannelScreen: View {
router.dismissModal() router.dismissModal()
} }
let result = await rostersStore.addRoster(ownerJid: ownerCredentials.bareJid, contactJID: contactJID, name: nil, groups: []) let result = await rostersStore.addRoster(ownerCredentials, contactJID: contactJID, name: nil, groups: [])
switch result { switch result {
case .success: case .success:

View file

@ -3,7 +3,7 @@ import SwiftUI
struct ContactsScreen: View { struct ContactsScreen: View {
@Environment(\.router) var router @Environment(\.router) var router
@EnvironmentObject var clientsStore: ClientsStore @EnvironmentObject var clientsStore: ClientsStore
@StateObject var rostersStore = RostersStore(clientsPublisher: ClientsStore.shared.$clients) @StateObject var rostersStore = RostersStore()
var body: some View { var body: some View {
ZStack { ZStack {