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 {
_ = try await connection.module(.roster).addItem(
jid: JID(jid),
@ -53,6 +55,17 @@ final class Client: ObservableObject {
func deleteRoster(_ roster: Roster) async throws {
_ = 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 {

View file

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

View file

@ -9,17 +9,13 @@ final class RostersStore: ObservableObject {
private var cancellable: AnyCancellable?
init(clientsPublisher: Published<[Client]>.Publisher) {
subscribeToClientsStore(clientsPublisher: clientsPublisher)
}
private func subscribeToClientsStore(clientsPublisher: Published<[Client]>.Publisher) {
init() {
let rostersPublisher = ValueObservation.tracking(Roster.fetchAll)
.publisher(in: Database.shared.dbQueue)
.receive(on: DispatchQueue.main)
.catch { _ in Just([]) }
cancellable = clientsPublisher
cancellable = ClientsStore.shared.$clients
.map { $0.filter { $0.state != .disabled } } // look rosters only for enabled clients
.flatMap { clients in
Publishers.MergeMany(clients.map { $0.$state })
@ -46,18 +42,29 @@ extension RostersStore {
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
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)
return .success
}
// add new roster
// 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
}
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
do {

View file

@ -130,7 +130,7 @@ struct AddContactOrChannelScreen: View {
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 {
case .success:

View file

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