From a3140c2d3e86537864d49d20bdf094360a6a8fd0 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 20 Dec 2024 20:33:09 +0400 Subject: [PATCH] ui prohibit sending to member if connection is not ready (#5399) --- .../Chat/Group/GroupMemberInfoView.swift | 73 ++++++++++++------- apps/ios/SimpleXChat/APITypes.swift | 4 + apps/ios/SimpleXChat/ChatTypes.swift | 8 ++ .../chat/simplex/common/model/ChatModel.kt | 7 ++ .../chat/simplex/common/model/SimpleXAPI.kt | 3 + .../views/chat/group/GroupMemberInfoView.kt | 50 ++++++++----- .../commonMain/resources/MR/base/strings.xml | 1 + 7 files changed, 98 insertions(+), 48 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index a18de1b349..30972f7242 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -162,7 +162,7 @@ struct GroupMemberInfoView: View { } .disabled( connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited + || !member.sendMsgEnabled ) if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { Button("Abort changing address") { @@ -170,7 +170,7 @@ struct GroupMemberInfoView: View { } .disabled( connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } - || connStats.ratchetSyncSendProhibited + || !member.sendMsgEnabled ) } smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary) @@ -297,7 +297,7 @@ struct GroupMemberInfoView: View { if let contactId = member.memberContactId { newDirectChatButton(contactId, width: buttonWidth) } else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false { - createMemberContactButton(width: buttonWidth) + createMemberContactButton(member, width: buttonWidth) } InfoViewButton(image: "phone.fill", title: "call", disabledLook: true, width: buttonWidth) { showSendMessageToEnableCallsAlert() } @@ -379,32 +379,41 @@ struct GroupMemberInfoView: View { } } - func createMemberContactButton(width: CGFloat) -> some View { - InfoViewButton(image: "message.fill", title: "message", width: width) { - if let connStats = connectionStats { - if connStats.ratchetSyncState == .ok { - progressIndicator = true - Task { - do { - let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId) - await MainActor.run { - progressIndicator = false - chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact))) - ItemsModel.shared.loadOpenChat(memberContact.id) { - dismissAllSheets(animated: true) - } - NetworkModel.shared.setContactNetworkStatus(memberContact, .connected) - } - } catch let error { - logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))") - let a = getErrorAlert(error, "Error creating member contact") - await MainActor.run { - progressIndicator = false - alert = .error(title: a.title, error: a.message) + func createMemberContactButton(_ member: GroupMember, width: CGFloat) -> some View { + InfoViewButton( + image: "message.fill", + title: "message", + disabledLook: + !( + member.sendMsgEnabled || + (member.activeConn?.connectionStats?.ratchetSyncAllowed ?? false) + ), + width: width + ) { + if member.sendMsgEnabled { + progressIndicator = true + Task { + do { + let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId) + await MainActor.run { + progressIndicator = false + chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact))) + ItemsModel.shared.loadOpenChat(memberContact.id) { + dismissAllSheets(animated: true) } + NetworkModel.shared.setContactNetworkStatus(memberContact, .connected) + } + } catch let error { + logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))") + let a = getErrorAlert(error, "Error creating member contact") + await MainActor.run { + progressIndicator = false + alert = .error(title: a.title, error: a.message) } } - } else if connStats.ratchetSyncAllowed { + } + } else if let connStats = connectionStats { + if connStats.ratchetSyncAllowed { alert = .someAlert(alert: SomeAlert( alert: Alert( title: Text("Fix connection?"), @@ -416,13 +425,21 @@ struct GroupMemberInfoView: View { ), id: "can't message member, fix connection" )) - } else { + } else if connStats.ratchetSyncInProgress { alert = .someAlert(alert: SomeAlert( alert: mkAlert( title: "Can't message member", message: "Encryption renegotiation in progress." ), - id: "can't message contact, encryption renegotiation in progress" + id: "can't message member, encryption renegotiation in progress" + )) + } else { + alert = .someAlert(alert: SomeAlert( + alert: mkAlert( + title: "Can't message member", + message: "Connection not ready." + ), + id: "can't message member, connection not ready" )) } } diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 66edb9f2b2..b6a8edb201 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -2034,6 +2034,10 @@ public struct ConnectionStats: Decodable, Hashable { public var ratchetSyncSendProhibited: Bool { [.required, .started, .agreed].contains(ratchetSyncState) } + + public var ratchetSyncInProgress: Bool { + [.started, .agreed].contains(ratchetSyncState) + } } public struct RcvQueueInfo: Codable, Hashable { diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index b81b0b7662..b1a318c896 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2010,6 +2010,14 @@ public struct GroupMember: Identifiable, Decodable, Hashable { public var activeConn: Connection? public var id: String { "#\(groupId) @\(groupMemberId)" } + public var ready: Bool { get { activeConn?.connStatus == .ready } } + public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } } + public var sendMsgEnabled: Bool { get { + sndReady + && memberCurrent + && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false) + && !(activeConn?.connDisabled ?? true) + } } public var displayName: String { get { let p = memberProfile diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index d407174e52..db90c2e573 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -1575,6 +1575,13 @@ data class GroupMember ( var activeConn: Connection? = null ): NamedChat { val id: String get() = "#$groupId @$groupMemberId" + val ready get() = activeConn?.connStatus == ConnStatus.Ready + val sndReady get() = ready || activeConn?.connStatus == ConnStatus.SndReady + val sendMsgEnabled get() = + sndReady + && memberCurrent + && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?: false) + && !(activeConn?.connDisabled ?: true) override val displayName: String get() { val p = memberProfile 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 e95fdb446f..7e3d8597b4 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 @@ -6032,6 +6032,9 @@ class ConnectionStats( val ratchetSyncSendProhibited: Boolean get() = listOf(RatchetSyncState.Required, RatchetSyncState.Started, RatchetSyncState.Agreed).contains(ratchetSyncState) + + val ratchetSyncInProgress: Boolean get() = + listOf(RatchetSyncState.Started, RatchetSyncState.Agreed).contains(ratchetSyncState) } @Serializable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index c9ac464438..e4e5494364 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -89,29 +89,34 @@ fun GroupMemberInfoView( } }, createMemberContact = { - if (connectionStats != null) { - if (connectionStats.ratchetSyncState == RatchetSyncState.Ok) { - withBGApi { - progressIndicator = true - val memberContact = chatModel.controller.apiCreateMemberContact(rhId, groupInfo.apiId, member.groupMemberId) - if (memberContact != null) { - val memberChat = Chat(remoteHostId = rhId, ChatInfo.Direct(memberContact), chatItems = arrayListOf()) - withChats { - addChat(memberChat) - openLoadedChat(memberChat) - } - closeAll() - chatModel.setContactNetworkStatus(memberContact, NetworkStatus.Connected()) + if (member.sendMsgEnabled) { + withBGApi { + progressIndicator = true + val memberContact = chatModel.controller.apiCreateMemberContact(rhId, groupInfo.apiId, member.groupMemberId) + if (memberContact != null) { + val memberChat = Chat(remoteHostId = rhId, ChatInfo.Direct(memberContact), chatItems = arrayListOf()) + withChats { + addChat(memberChat) + openLoadedChat(memberChat) } - progressIndicator = false + closeAll() + chatModel.setContactNetworkStatus(memberContact, NetworkStatus.Connected()) } - } else if (connectionStats.ratchetSyncAllowed) { + progressIndicator = false + } + } else if (connectionStats != null) { + if (connectionStats.ratchetSyncAllowed) { showFixConnectionAlert(syncConnection = { syncMemberConnection() }) - } else { + } else if (connectionStats.ratchetSyncInProgress) { AlertManager.shared.showAlertMsg( generalGetString(MR.strings.cant_send_message_to_member_alert_title), generalGetString(MR.strings.encryption_renegotiation_in_progress) ) + } else { + AlertManager.shared.showAlertMsg( + generalGetString(MR.strings.cant_send_message_to_member_alert_title), + generalGetString(MR.strings.connection_not_ready) + ) } } }, @@ -366,7 +371,11 @@ fun GroupMemberInfoLayout( if (contactId != null) { OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { openDirectChat(contactId) }) // legacy - only relevant for direct contacts created when joining group } else { - OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { createMemberContact() }) + OpenChatButton( + modifier = Modifier.fillMaxWidth(0.33f), + disabledLook = !(member.sendMsgEnabled || (member.activeConn?.connectionStats?.ratchetSyncAllowed ?: false)), + onClick = { createMemberContact() } + ) } InfoViewActionButton(modifier = Modifier.fillMaxWidth(0.5f), painterResource(MR.images.ic_call), generalGetString(MR.strings.info_view_call_button), disabled = false, disabledLook = true, onClick = { showSendMessageToEnableCallsAlert() @@ -437,12 +446,12 @@ fun GroupMemberInfoLayout( SectionDividerSpaced() SectionView(title = stringResource(MR.strings.conn_stats_section_title_servers)) { SwitchAddressButton( - disabled = cStats.rcvQueuesInfo.any { it.rcvSwitchStatus != null } || cStats.ratchetSyncSendProhibited, + disabled = cStats.rcvQueuesInfo.any { it.rcvSwitchStatus != null } || !member.sendMsgEnabled, switchAddress = switchMemberAddress ) if (cStats.rcvQueuesInfo.any { it.rcvSwitchStatus != null }) { AbortSwitchAddressButton( - disabled = cStats.rcvQueuesInfo.any { it.rcvSwitchStatus != null && !it.canAbortSwitch } || cStats.ratchetSyncSendProhibited, + disabled = cStats.rcvQueuesInfo.any { it.rcvSwitchStatus != null && !it.canAbortSwitch } || !member.sendMsgEnabled, abortSwitchAddress = abortSwitchMemberAddress ) } @@ -602,6 +611,7 @@ fun RemoveMemberButton(onClick: () -> Unit) { @Composable fun OpenChatButton( modifier: Modifier, + disabledLook: Boolean = false, onClick: () -> Unit ) { InfoViewActionButton( @@ -609,7 +619,7 @@ fun OpenChatButton( icon = painterResource(MR.images.ic_chat_bubble), title = generalGetString(MR.strings.info_view_message_button), disabled = false, - disabledLook = false, + disabledLook = disabledLook, onClick = onClick ) } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 34788b5bde..a209126a89 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1696,6 +1696,7 @@ Can\'t call group member Send message to enable calls. Can\'t message group member + Connection not ready. Welcome message