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 c155010a28..795baece0c 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 @@ -1172,18 +1172,22 @@ data class Connection( val pqSndEnabled: Boolean? = null, val pqRcvEnabled: Boolean? = null, val connectionStats: ConnectionStats? = null, - val authErrCounter: Int + val authErrCounter: Int, + val quotaErrCounter: Int ) { val id: ChatId get() = ":$connId" val connDisabled: Boolean get() = authErrCounter >= 10 // authErrDisableCount in core + val connInactive: Boolean + get() = quotaErrCounter >= 5 // quotaErrInactiveCount in core + val connPQEnabled: Boolean get() = pqSndEnabled == true && pqRcvEnabled == true companion object { - val sampleData = Connection(connId = 1, agentConnId = "abc", connStatus = ConnStatus.Ready, connLevel = 0, viaGroupLink = false, peerChatVRange = VersionRange(1, 1), customUserProfileId = null, pqSupport = false, pqEncryption = false, authErrCounter = 0) + val sampleData = Connection(connId = 1, agentConnId = "abc", connStatus = ConnStatus.Ready, connLevel = 0, viaGroupLink = false, peerChatVRange = VersionRange(1, 1), customUserProfileId = null, pqSupport = false, pqEncryption = false, authErrCounter = 0, quotaErrCounter = 0) } } @@ -2352,6 +2356,48 @@ enum class SndCIStatusProgress { @SerialName("complete") Complete; } +@Serializable +sealed class GroupSndStatus { + @Serializable @SerialName("new") class New: GroupSndStatus() + @Serializable @SerialName("forwarded") class Forwarded: GroupSndStatus() + @Serializable @SerialName("inactive") class Inactive: GroupSndStatus() + @Serializable @SerialName("sent") class Sent: GroupSndStatus() + @Serializable @SerialName("rcvd") class Rcvd(val msgRcptStatus: MsgReceiptStatus): GroupSndStatus() + @Serializable @SerialName("error") class Error(val agentError: SndError): GroupSndStatus() + @Serializable @SerialName("warning") class Warning(val agentError: SndError): GroupSndStatus() + @Serializable @SerialName("invalid") class Invalid(val text: String): GroupSndStatus() + + fun statusIcon( + primaryColor: Color, + metaColor: Color = CurrentColors.value.colors.secondary, + paleMetaColor: Color = CurrentColors.value.colors.secondary + ): Pair = + when (this) { + is New -> MR.images.ic_more_horiz to metaColor + is Forwarded -> MR.images.ic_chevron_right_2 to metaColor + is Inactive -> MR.images.ic_person_off to metaColor + is Sent -> MR.images.ic_check_filled to metaColor + is Rcvd -> when(this.msgRcptStatus) { + MsgReceiptStatus.Ok -> MR.images.ic_double_check to metaColor + MsgReceiptStatus.BadMsgHash -> MR.images.ic_double_check to Color.Red + } + is Error -> MR.images.ic_close to Color.Red + is Warning -> MR.images.ic_warning_filled to WarningOrange + is Invalid -> MR.images.ic_question_mark to metaColor + } + + val statusInto: Pair? get() = when (this) { + is New -> null + is Forwarded -> generalGetString(MR.strings.message_forwarded_title) to generalGetString(MR.strings.message_forwarded_desc) + is Inactive -> generalGetString(MR.strings.member_inactive_title) to generalGetString(MR.strings.member_inactive_desc) + is Sent -> null + is Rcvd -> null + is Error -> generalGetString(MR.strings.message_delivery_error_title) to agentError.errorInfo + is Warning -> generalGetString(MR.strings.message_delivery_warning_title) to agentError.errorInfo + is Invalid -> "Invalid status" to this.text + } +} + @Serializable sealed class CIDeleted { @Serializable @SerialName("deleted") class Deleted(val deletedTs: Instant?): CIDeleted() @@ -3466,7 +3512,7 @@ data class ChatItemVersion( @Serializable data class MemberDeliveryStatus( val groupMemberId: Long, - val memberDeliveryStatus: CIStatus, + val memberDeliveryStatus: GroupSndStatus, val sentViaProxy: Boolean? ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemInfoView.kt index c23a6ad00e..797d8e66b8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemInfoView.kt @@ -330,7 +330,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools } @Composable - fun MemberDeliveryStatusView(member: GroupMember, status: CIStatus, sentViaProxy: Boolean?) { + fun MemberDeliveryStatusView(member: GroupMember, status: GroupSndStatus, sentViaProxy: Boolean?) { SectionItemView( padding = PaddingValues(horizontal = 0.dp) ) { @@ -355,7 +355,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools ) } } - val statusIcon = status.statusIcon(MaterialTheme.colors.primary, CurrentColors.value.colors.secondary) + val (icon, statusColor) = status.statusIcon(MaterialTheme.colors.primary, CurrentColors.value.colors.secondary) var modifier = Modifier.size(36.dp).clip(RoundedCornerShape(20.dp)) val info = status.statusInto if (info != null) { @@ -367,20 +367,11 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools } } Box(modifier, contentAlignment = Alignment.Center) { - if (statusIcon != null) { - val (icon, statusColor) = statusIcon - Icon( - painterResource(icon), - contentDescription = null, - tint = statusColor - ) - } else { - Icon( - painterResource(MR.images.ic_more_horiz), - contentDescription = null, - tint = CurrentColors.value.colors.secondary - ) - } + Icon( + painterResource(icon), + contentDescription = null, + tint = statusColor + ) } } } @@ -520,7 +511,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools } } -private fun membersStatuses(chatModel: ChatModel, memberDeliveryStatuses: List): List> { +private fun membersStatuses(chatModel: ChatModel, memberDeliveryStatuses: List): List> { return memberDeliveryStatuses.mapNotNull { mds -> chatModel.getGroupMember(mds.groupMemberId)?.let { mem -> Triple(mem, mds.memberDeliveryStatus, mds.sentViaProxy) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index b60c1f2496..c91bc3bcfc 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -390,6 +390,16 @@ private fun MemberRow(member: GroupMember, user: Boolean = false, onClick: (() - } } + fun memberConnStatus(): String { + return if (member.activeConn?.connDisabled == true) { + generalGetString(MR.strings.member_info_member_disabled) + } else if (member.activeConn?.connDisabled == true) { + generalGetString(MR.strings.member_info_member_inactive) + } else { + member.memberStatus.shortText + } + } + Row( Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, @@ -412,8 +422,8 @@ private fun MemberRow(member: GroupMember, user: Boolean = false, onClick: (() - color = if (member.memberIncognito) Indigo else Color.Unspecified ) } - val s = member.memberStatus.shortText - val statusDescr = if (user) String.format(generalGetString(MR.strings.group_info_member_you), s) else s + val statusDescr = + if (user) String.format(generalGetString(MR.strings.group_info_member_you), member.memberStatus.shortText) else memberConnStatus() Text( statusDescr, color = MaterialTheme.colors.secondary, 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 2799904b71..98b9ca729f 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 @@ -358,13 +358,6 @@ fun GroupMemberInfoLayout( } else { InfoRow(stringResource(MR.strings.role_in_group), member.memberRole.text) } - val conn = member.activeConn - if (conn != null) { - val connLevelDesc = - if (conn.connLevel == 0) stringResource(MR.strings.conn_level_desc_direct) - else String.format(generalGetString(MR.strings.conn_level_desc_indirect), conn.connLevel) - InfoRow(stringResource(MR.strings.info_row_connection), connLevelDesc) - } } if (cStats != null) { SectionDividerSpaced() @@ -401,6 +394,13 @@ fun GroupMemberInfoLayout( SectionView(title = stringResource(MR.strings.section_title_for_console)) { InfoRow(stringResource(MR.strings.info_row_local_name), member.localDisplayName) InfoRow(stringResource(MR.strings.info_row_database_id), member.groupMemberId.toString()) + val conn = member.activeConn + if (conn != null) { + val connLevelDesc = + if (conn.connLevel == 0) stringResource(MR.strings.conn_level_desc_direct) + else String.format(generalGetString(MR.strings.conn_level_desc_indirect), conn.connLevel) + InfoRow(stringResource(MR.strings.info_row_connection), connLevelDesc) + } SectionItemView({ withBGApi { val info = controller.apiGroupMemberQueueInfo(rhId, groupInfo.apiId, member.groupMemberId) 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 3452e6ff60..f3d78eef20 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -324,6 +324,11 @@ Forward Download + Message forwarded + Connection is not established yet, message is forwarded by group admin. + Member inactive + Message is pending delivery when member becomes active. + edited sent @@ -1461,6 +1466,8 @@ Messages from %s will be shown! Blocked by admin blocked + disabled + inactive MEMBER Role Change role diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_chevron_right_2.svg b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_chevron_right_2.svg new file mode 100644 index 0000000000..ef2a9e9865 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_chevron_right_2.svg @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_person_off.svg b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_person_off.svg new file mode 100644 index 0000000000..a583950023 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_person_off.svg @@ -0,0 +1 @@ + \ No newline at end of file