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