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