diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 365f23c33e..a099069f77 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -21,6 +21,8 @@ public let CURRENT_CHAT_VERSION: Int = 2 // version range that supports establishing direct connection with a group member (xGrpDirectInvVRange in core) public let CREATE_MEMBER_CONTACT_VRANGE = VersionRange(minVersion: 2, maxVersion: CURRENT_CHAT_VERSION) +private let networkStatusesLock = DispatchQueue(label: "chat.simplex.app.network-statuses.lock") + enum TerminalItem: Identifiable { case cmd(Date, ChatCommand) case resp(Date, ChatResponse) @@ -1566,34 +1568,30 @@ func processReceivedMsg(_ res: ChatResponse) async { m.removeChat(mergedContact.id) } } - case let .contactsSubscribed(_, contactRefs): - await updateContactsStatus(contactRefs, status: .connected) - case let .contactsDisconnected(_, contactRefs): - await updateContactsStatus(contactRefs, status: .disconnected) - case let .contactSubSummary(_, contactSubscriptions): - await MainActor.run { - for sub in contactSubscriptions { -// no need to update contact here, and it is slow -// if active(user) { -// m.updateContact(sub.contact) -// } - if let err = sub.contactError { - processContactSubError(sub.contact, err) - } else { - m.setContactNetworkStatus(sub.contact, .connected) - } - } - } case let .networkStatus(status, connections): - await MainActor.run { + // dispatch queue to synchronize access + networkStatusesLock.sync { + var ns = m.networkStatuses + // slow loop is on the background thread for cId in connections { - m.networkStatuses[cId] = status + ns[cId] = status + } + // fast model update is on the main thread + DispatchQueue.main.sync { + m.networkStatuses = ns } } case let .networkStatuses(_, statuses): () - await MainActor.run { + // dispatch queue to synchronize access + networkStatusesLock.sync { + var ns = m.networkStatuses + // slow loop is on the background thread for s in statuses { - m.networkStatuses[s.agentConnId] = s.networkStatus + ns[s.agentConnId] = s.networkStatus + } + // fast model update is on the main thread + DispatchQueue.main.sync { + m.networkStatuses = ns } } case let .newChatItem(user, aChatItem): @@ -1944,26 +1942,6 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async { } } -func updateContactsStatus(_ contactRefs: [ContactRef], status: NetworkStatus) async { - let m = ChatModel.shared - await MainActor.run { - for c in contactRefs { - m.networkStatuses[c.agentConnId] = status - } - } -} - -func processContactSubError(_ contact: Contact, _ chatError: ChatError) { - let m = ChatModel.shared - var err: String - switch chatError { - case .errorAgent(agentError: .BROKER(_, .NETWORK)): err = "network" - case .errorAgent(agentError: .SMP(smpErr: .AUTH)): err = "contact deleted" - default: err = String(describing: chatError) - } - m.setContactNetworkStatus(contact, .error(connectionError: err)) -} - func refreshCallInvitations() throws { let m = ChatModel.shared let callInvitations = try justRefreshCallInvitations() diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index efac3526ef..b18022cfec 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -557,11 +557,6 @@ public enum ChatResponse: Decodable, Error { case contactRequestRejected(user: UserRef) case contactUpdated(user: UserRef, toContact: Contact) case groupMemberUpdated(user: UserRef, groupInfo: GroupInfo, fromMember: GroupMember, toMember: GroupMember) - // TODO remove events below - case contactsSubscribed(server: String, contactRefs: [ContactRef]) - case contactsDisconnected(server: String, contactRefs: [ContactRef]) - case contactSubSummary(user: UserRef, contactSubscriptions: [ContactSubStatus]) - // TODO remove events above case networkStatus(networkStatus: NetworkStatus, connections: [String]) case networkStatuses(user_: UserRef?, networkStatuses: [ConnNetworkStatus]) case groupSubscribed(user: UserRef, groupInfo: GroupRef) @@ -724,9 +719,6 @@ public enum ChatResponse: Decodable, Error { case .contactRequestRejected: return "contactRequestRejected" case .contactUpdated: return "contactUpdated" case .groupMemberUpdated: return "groupMemberUpdated" - case .contactsSubscribed: return "contactsSubscribed" - case .contactsDisconnected: return "contactsDisconnected" - case .contactSubSummary: return "contactSubSummary" case .networkStatus: return "networkStatus" case .networkStatuses: return "networkStatuses" case .groupSubscribed: return "groupSubscribed" @@ -885,9 +877,6 @@ public enum ChatResponse: Decodable, Error { case .contactRequestRejected: return noDetails case let .contactUpdated(u, toContact): return withUser(u, String(describing: toContact)) case let .groupMemberUpdated(u, groupInfo, fromMember, toMember): return withUser(u, "groupInfo: \(groupInfo)\nfromMember: \(fromMember)\ntoMember: \(toMember)") - case let .contactsSubscribed(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))" - case let .contactsDisconnected(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))" - case let .contactSubSummary(u, contactSubscriptions): return withUser(u, String(describing: contactSubscriptions)) case let .networkStatus(status, conns): return "networkStatus: \(String(describing: status))\nconnections: \(String(describing: conns))" case let .networkStatuses(u, statuses): return withUser(u, String(describing: statuses)) case let .groupSubscribed(u, groupInfo): return withUser(u, String(describing: groupInfo)) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 4fe502e239..3811bca170 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -1753,6 +1753,8 @@ object ChatController { chatModel.removeChat(rhId, r.mergedContact.id) } } + // ContactsSubscribed, ContactsDisconnected and ContactSubSummary are only used in CLI, + // They have to be used here for remote desktop to process these status updates. is CR.ContactsSubscribed -> updateContactsStatus(r.contactRefs, NetworkStatus.Connected()) is CR.ContactsDisconnected -> updateContactsStatus(r.contactRefs, NetworkStatus.Disconnected()) is CR.ContactSubSummary -> {