desktop, android: show inactive and forwarded group message status; show inactive members (#4426)

* multiplatform: differentiate inactive and forwarded group snd statuses; show member connection disabled/inactive

* forward icon

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
spaced4ndy
2024-07-09 21:28:01 +01:00
committed by GitHub
co-authored by Evgeny Poberezkin
parent 49c29c74df
commit 93a4c0854e
7 changed files with 94 additions and 29 deletions
@@ -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<ImageResource, Color> =
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<String, String>? 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?
)
@@ -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<MemberDeliveryStatus>): List<Triple<GroupMember, CIStatus, Boolean?>> {
private fun membersStatuses(chatModel: ChatModel, memberDeliveryStatuses: List<MemberDeliveryStatus>): List<Triple<GroupMember, GroupSndStatus, Boolean?>> {
return memberDeliveryStatuses.mapNotNull { mds ->
chatModel.getGroupMember(mds.groupMemberId)?.let { mem ->
Triple(mem, mds.memberDeliveryStatus, mds.sentViaProxy)
@@ -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,
@@ -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)
@@ -324,6 +324,11 @@
<string name="forward_chat_item">Forward</string>
<string name="download_file">Download</string>
<string name="message_forwarded_title">Message forwarded</string>
<string name="message_forwarded_desc">Connection is not established yet, message is forwarded by group admin.</string>
<string name="member_inactive_title">Member inactive</string>
<string name="member_inactive_desc">Message is pending delivery when member becomes active.</string>
<!-- CIMetaView.kt -->
<string name="icon_descr_edited">edited</string>
<string name="icon_descr_sent_msg_status_sent">sent</string>
@@ -1461,6 +1466,8 @@
<string name="unblock_member_desc">Messages from %s will be shown!</string>
<string name="member_blocked_by_admin">Blocked by admin</string>
<string name="member_info_member_blocked">blocked</string>
<string name="member_info_member_disabled">disabled</string>
<string name="member_info_member_inactive">inactive</string>
<string name="member_info_section_title_member">MEMBER</string>
<string name="role_in_group">Role</string>
<string name="change_role">Change role</string>
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960" width="20px" height="20px">
<g transform="matrix(1, 0, 0, 1, -160, 960)">
<path
d="m592.5-482-314-314q-10.5-10.5-10.75-26.25t10.75-27.25q11-11.5 27-11.5t27.5 11.5L661-522q8.5 8.5 12.25 18.75T677-482q0 11-3.75 21.25T661-442L332.5-113.5q-11.5 11.5-27.25 11t-26.75-12q-11-11-11.25-26.5t11.25-27l314-314Z" />
</g>
<g transform="matrix(1, 0, 0, 1, 160, 960)">
<path
d="m592.5-482-314-314q-10.5-10.5-10.75-26.25t10.75-27.25q11-11.5 27-11.5t27.5 11.5L661-522q8.5 8.5 12.25 18.75T677-482q0 11-3.75 21.25T661-442L332.5-113.5q-11.5 11.5-27.25 11t-26.75-12q-11-11-11.25-26.5t11.25-27l314-314Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 715 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#000000"><path d="m790.5-88-79-78.5H222q-23.97 0-40.73-16.77Q164.5-200.03 164.5-224v-33.52q0-37.48 18.75-63.98t48.25-40q60-27 115.25-41.75T458-420L87.09-790.91q-8.59-8.59-8.34-20.59t9-20.5q8.75-8.5 20.75-8.5t20.61 8.61l702.78 703.28q8.61 8.43 8.61 20.27T831.75-88Q823-79.5 811-79.5T790.5-88ZM222-224h432L515.48-362.5q-8.48-.5-17.48-.75t-17.98-.25q-56.54 0-109.53 11.5T254.5-310q-14 7-23.25 21.73T222-257.26V-224Zm505.5-137.5q31 13.5 49.5 40.25T795.5-257v9.5L652-391q18 6 37 13.75t38.5 15.75ZM548-495l-46-45.8q30.23-7.2 49.11-31.2Q570-596 570-628q0-38-26-64t-64-26q-32 0-56 19t-31 49l-46-46q18.5-38 54.5-58.75T480-775.5q62 0 104.75 42.75T627.5-628q0 42.5-20.75 78.5T548-495Zm106 271H222h432ZM448-596Z"/></svg>

After

Width:  |  Height:  |  Size: 802 B