mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-01 20:08:57 +00:00
ios: speed up network status handling (#4678)
* move network status into a separate model * reduce network model observation scope (#4679) * dont pass chat --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
co-authored by
Evgeny Poberezkin
parent
7cb3a499b2
commit
c3f67aff69
@@ -100,6 +100,29 @@ class ItemsModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkModel: ObservableObject {
|
||||
// map of connections network statuses, key is agent connection id
|
||||
@Published var networkStatuses: Dictionary<String, NetworkStatus> = [:]
|
||||
|
||||
static let shared = NetworkModel()
|
||||
|
||||
private init() { }
|
||||
|
||||
func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) {
|
||||
if let conn = contact.activeConn {
|
||||
networkStatuses[conn.agentConnId] = status
|
||||
}
|
||||
}
|
||||
|
||||
func contactNetworkStatus(_ contact: Contact) -> NetworkStatus {
|
||||
if let conn = contact.activeConn {
|
||||
networkStatuses[conn.agentConnId] ?? .unknown
|
||||
} else {
|
||||
.unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatModel: ObservableObject {
|
||||
@Published var onboardingStage: OnboardingStage?
|
||||
@Published var setDeliveryReceipts = false
|
||||
@@ -122,8 +145,6 @@ final class ChatModel: ObservableObject {
|
||||
// list of chat "previews"
|
||||
@Published var chats: [Chat] = []
|
||||
@Published var deletedChats: Set<String> = []
|
||||
// map of connections network statuses, key is agent connection id
|
||||
@Published var networkStatuses: Dictionary<String, NetworkStatus> = [:]
|
||||
// current chat
|
||||
@Published var chatId: String?
|
||||
var chatItemStatuses: Dictionary<Int64, CIStatus> = [:]
|
||||
@@ -909,20 +930,6 @@ final class ChatModel: ObservableObject {
|
||||
while i < maxIx && inView(i) { i += 1 }
|
||||
return im.reversedChatItems[min(i - 1, maxIx)]
|
||||
}
|
||||
|
||||
func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) {
|
||||
if let conn = contact.activeConn {
|
||||
networkStatuses[conn.agentConnId] = status
|
||||
}
|
||||
}
|
||||
|
||||
func contactNetworkStatus(_ contact: Contact) -> NetworkStatus {
|
||||
if let conn = contact.activeConn {
|
||||
networkStatuses[conn.agentConnId] ?? .unknown
|
||||
} else {
|
||||
.unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ShowingInvitation {
|
||||
|
||||
@@ -1167,7 +1167,7 @@ func acceptContactRequest(incognito: Bool, contactRequest: UserContactRequest) a
|
||||
let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: [])
|
||||
await MainActor.run {
|
||||
ChatModel.shared.replaceChat(contactRequest.id, chat)
|
||||
ChatModel.shared.setContactNetworkStatus(contact, .connected)
|
||||
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
|
||||
}
|
||||
if contact.sndReady {
|
||||
DispatchQueue.main.async {
|
||||
@@ -1659,6 +1659,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
await TerminalItems.shared.add(.resp(.now, res))
|
||||
}
|
||||
let m = ChatModel.shared
|
||||
let n = NetworkModel.shared
|
||||
logger.debug("processReceivedMsg: \(res.responseType)")
|
||||
switch res {
|
||||
case let .contactDeletedByContact(user, contact):
|
||||
@@ -1681,7 +1682,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
NtfManager.shared.notifyContactConnected(user, contact)
|
||||
}
|
||||
await MainActor.run {
|
||||
m.setContactNetworkStatus(contact, .connected)
|
||||
n.setContactNetworkStatus(contact, .connected)
|
||||
}
|
||||
case let .contactConnecting(user, contact):
|
||||
if active(user) && contact.directOrUsed {
|
||||
@@ -1704,7 +1705,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
}
|
||||
await MainActor.run {
|
||||
m.setContactNetworkStatus(contact, .connected)
|
||||
n.setContactNetworkStatus(contact, .connected)
|
||||
}
|
||||
case let .receivedContactRequest(user, contactRequest):
|
||||
if active(user) {
|
||||
@@ -1746,27 +1747,27 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
case let .networkStatus(status, connections):
|
||||
// dispatch queue to synchronize access
|
||||
networkStatusesLock.sync {
|
||||
var ns = m.networkStatuses
|
||||
var ns = n.networkStatuses
|
||||
// slow loop is on the background thread
|
||||
for cId in connections {
|
||||
ns[cId] = status
|
||||
}
|
||||
// fast model update is on the main thread
|
||||
DispatchQueue.main.sync {
|
||||
m.networkStatuses = ns
|
||||
n.networkStatuses = ns
|
||||
}
|
||||
}
|
||||
case let .networkStatuses(_, statuses): ()
|
||||
// dispatch queue to synchronize access
|
||||
networkStatusesLock.sync {
|
||||
var ns = m.networkStatuses
|
||||
var ns = n.networkStatuses
|
||||
// slow loop is on the background thread
|
||||
for s in statuses {
|
||||
ns[s.agentConnId] = s.networkStatus
|
||||
}
|
||||
// fast model update is on the main thread
|
||||
DispatchQueue.main.sync {
|
||||
m.networkStatuses = ns
|
||||
n.networkStatuses = ns
|
||||
}
|
||||
}
|
||||
case let .newChatItem(user, aChatItem):
|
||||
@@ -1907,7 +1908,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
if let contact = memberContact {
|
||||
await MainActor.run {
|
||||
m.setContactNetworkStatus(contact, .connected)
|
||||
n.setContactNetworkStatus(contact, .connected)
|
||||
}
|
||||
}
|
||||
case let .groupUpdated(user, toGroup):
|
||||
@@ -2132,12 +2133,13 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
|
||||
func switchToLocalSession() {
|
||||
let m = ChatModel.shared
|
||||
let n = NetworkModel.shared
|
||||
m.remoteCtrlSession = nil
|
||||
do {
|
||||
m.users = try listUsers()
|
||||
try getUserChatData()
|
||||
let statuses = (try apiGetNetworkStatuses()).map { s in (s.agentConnId, s.networkStatus) }
|
||||
m.networkStatuses = Dictionary(uniqueKeysWithValues: statuses)
|
||||
n.networkStatuses = Dictionary(uniqueKeysWithValues: statuses)
|
||||
} catch let error {
|
||||
logger.debug("error updating chat data: \(responseError(error))")
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ struct ChatInfoView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@ObservedObject var networkModel = NetworkModel.shared
|
||||
@ObservedObject var chat: Chat
|
||||
@State var contact: Contact
|
||||
@State var localAlias: String
|
||||
@@ -502,14 +503,14 @@ struct ChatInfoView: View {
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.font(.system(size: 14))
|
||||
Spacer()
|
||||
Text(chatModel.contactNetworkStatus(contact).statusString)
|
||||
Text(networkModel.contactNetworkStatus(contact).statusString)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
serverImage()
|
||||
}
|
||||
}
|
||||
|
||||
private func serverImage() -> some View {
|
||||
let status = chatModel.contactNetworkStatus(contact)
|
||||
let status = networkModel.contactNetworkStatus(contact)
|
||||
return Image(systemName: status.imageName)
|
||||
.foregroundColor(status == .connected ? .green : theme.colors.secondary)
|
||||
.font(.system(size: 12))
|
||||
@@ -557,7 +558,7 @@ struct ChatInfoView: View {
|
||||
private func networkStatusAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("Network status"),
|
||||
message: Text(chatModel.contactNetworkStatus(contact).statusExplanation)
|
||||
message: Text(networkModel.contactNetworkStatus(contact).statusExplanation)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ struct GroupMemberInfoView: View {
|
||||
ItemsModel.shared.loadOpenChat(memberContact.id) {
|
||||
dismissAllSheets(animated: true)
|
||||
}
|
||||
chatModel.setContactNetworkStatus(memberContact, .connected)
|
||||
NetworkModel.shared.setContactNetworkStatus(memberContact, .connected)
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))")
|
||||
|
||||
@@ -376,17 +376,7 @@ struct ChatPreviewView: View {
|
||||
switch chat.chatInfo {
|
||||
case let .direct(contact):
|
||||
if contact.active && contact.activeConn != nil {
|
||||
switch (chatModel.contactNetworkStatus(contact)) {
|
||||
case .connected: incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
||||
case .error:
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
default:
|
||||
ProgressView()
|
||||
}
|
||||
NetworkStatusView(contact: contact, size: size)
|
||||
} else {
|
||||
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
||||
}
|
||||
@@ -400,6 +390,30 @@ struct ChatPreviewView: View {
|
||||
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
struct NetworkStatusView: View {
|
||||
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@ObservedObject var networkModel = NetworkModel.shared
|
||||
|
||||
let contact: Contact
|
||||
let size: CGFloat
|
||||
|
||||
var body: some View {
|
||||
let dynamicChatInfoSize = dynamicSize(userFont).chatInfoSize
|
||||
switch (networkModel.contactNetworkStatus(contact)) {
|
||||
case .connected: incognitoIcon(contact.contactConnIncognito, theme.colors.secondary, size: size)
|
||||
case .error:
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
default:
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func incognitoIcon(_ incognito: Bool, _ secondaryColor: Color, size: CGFloat) -> some View {
|
||||
|
||||
Reference in New Issue
Block a user