update UI, tests

This commit is contained in:
Evgeny @ SimpleX Chat
2026-06-11 16:40:02 +00:00
parent 57b465c630
commit 64a4c0c9ee
11 changed files with 112 additions and 39 deletions
@@ -1994,7 +1994,10 @@ data class Profile(
override val localAlias : String = "",
val contactLink: String? = null,
val preferences: ChatPreferences? = null,
val peerType: ChatPeerType? = null
val peerType: ChatPeerType? = null,
// opaque badge proof from the wire profile: not interpreted by the UI (display uses crypto-free LocalBadge),
// but preserved so passing a link profile back to the core (apiPrepareContact) keeps the proof
val badge: JsonElement? = null
): NamedChat {
val profileViewName: String
get() {
@@ -2330,7 +2333,9 @@ enum class MemberCriteria {
data class ContactShortLinkData (
val profile: Profile,
val message: MsgContent?,
val business: Boolean
val business: Boolean,
// set by the core when building the connection plan: the link profile's badge, verified and crypto-free
val localBadge: LocalBadge? = null
)
@Serializable
@@ -2779,7 +2784,7 @@ class UserContactRequest (
val contactRequestId: Long,
val cReqChatVRange: VersionRange,
override val localDisplayName: String,
val profile: Profile,
val profile: LocalProfile,
override val createdAt: Instant,
override val updatedAt: Instant
): SomeChat, NamedChat {
@@ -2805,7 +2810,7 @@ class UserContactRequest (
contactRequestId = 1,
cReqChatVRange = VersionRange(1, 1),
localDisplayName = "alice",
profile = Profile.sampleData,
profile = LocalProfile.sampleData,
createdAt = Clock.System.now(),
updatedAt = Clock.System.now()
)
@@ -2051,7 +2051,8 @@ fun BoxScope.ChatItemsList(
}
Row(Modifier.graphicsLayer { translationX = selectionOffset.toPx() }) {
val member = cItem.chatDir.groupMember
Box(Modifier.clickable { showMemberInfo(chatInfo.groupInfo, member) }) {
// zIndex draws the avatar (and its badge overflow) above the message bubble tail
Box(Modifier.zIndex(1f).clickable { showMemberInfo(chatInfo.groupInfo, member) }) {
MemberImage(member)
}
Box(modifier = Modifier.padding(top = 2.dp, start = 4.dp).chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)) {
@@ -352,7 +352,9 @@ fun ContactCheckRow(
}
} else null
) {
ProfileImage(size = 36.dp, contact.image)
BadgedProfileImage(36.dp, if (contact.active) contact.profile.localBadge else null) {
ProfileImage(size = 36.dp, contact.image)
}
Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON))
Text(
contact.chatViewName,
@@ -389,7 +389,7 @@ fun ChatPreviewView(
Box(contentAlignment = Alignment.Center) {
Row {
Box(contentAlignment = Alignment.BottomEnd) {
ChatInfoImage(cInfo, size = 72.dp * fontSizeSqrtMultiplier)
ChatInfoImage(cInfo, size = 72.dp * fontSizeSqrtMultiplier, tappableBadge = true)
Box(Modifier.padding(end = 6.sp.toDp(), bottom = 6.sp.toDp())) {
chatPreviewImageOverlayIcon()
}
@@ -102,7 +102,10 @@ private fun SharePreviewView(chat: Chat, disabled: Boolean) {
} else if (chat.chatInfo is ChatInfo.Group) {
ProfileImage(size = 42.dp, chat.chatInfo.image, icon = MR.images.ic_supervised_user_circle_filled)
} else {
ProfileImage(size = 42.dp, chat.chatInfo.image)
val ct = (chat.chatInfo as? ChatInfo.Direct)?.contact
BadgedProfileImage(42.dp, if (ct?.active == true) ct.profile.localBadge else null) {
ProfileImage(size = 42.dp, chat.chatInfo.image)
}
}
Text(
chat.chatInfo.chatViewName, maxLines = 1, overflow = TextOverflow.Ellipsis,
@@ -38,7 +38,12 @@ fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme
is ChatInfo.Direct -> chatInfo.contact.chatIconName
else -> MR.images.ic_account_circle_filled
}
val badge = if (chatInfo is ChatInfo.Direct) chatInfo.contact.profile.localBadge else null
// a deleted (inactive) contact shows the inactive icon instead of the badge
val badge = when {
chatInfo is ChatInfo.Direct && chatInfo.contact.active -> chatInfo.contact.profile.localBadge
chatInfo is ChatInfo.ContactRequest -> chatInfo.contactRequest.profile.localBadge
else -> null
}
BadgedProfileImage(size, badge, onBadgeClick = if (tappableBadge) badge?.let { b -> { showBadgeInfoAlert(b) } } else null) {
ProfileImage(size, chatInfo.image, icon, if (chatInfo is ChatInfo.Local) NoteFolderIconColor else iconColor)
}
@@ -468,11 +468,13 @@ private fun showOpenKnownContactAlert(chatModel: ChatModel, rhId: Long?, close:
profileName = contact.profile.displayName,
profileFullName = contact.profile.fullName,
profileImage = {
ProfileImage(
size = alertProfileImageSize,
image = contact.profile.image,
icon = contact.chatIconName
)
BadgedProfileImage(alertProfileImageSize, if (contact.active) contact.profile.localBadge else null) {
ProfileImage(
size = alertProfileImageSize,
image = contact.profile.image,
icon = contact.chatIconName
)
}
},
confirmText = generalGetString(if (contact.nextConnectPrepared) MR.strings.connect_plan_open_new_chat else MR.strings.connect_plan_open_chat),
onConfirm = {
@@ -624,14 +626,16 @@ fun showPrepareContactAlert(
profileName = contactShortLinkData.profile.displayName,
profileFullName = contactShortLinkData.profile.fullName,
profileImage = {
ProfileImage(
size = alertProfileImageSize,
image = contactShortLinkData.profile.image,
icon =
if (contactShortLinkData.business) MR.images.ic_work_filled_padded
else if (contactShortLinkData.profile.peerType == ChatPeerType.Bot) MR.images.ic_cube
else MR.images.ic_account_circle_filled
)
BadgedProfileImage(alertProfileImageSize, contactShortLinkData.localBadge) {
ProfileImage(
size = alertProfileImageSize,
image = contactShortLinkData.profile.image,
icon =
if (contactShortLinkData.business) MR.images.ic_work_filled_padded
else if (contactShortLinkData.profile.peerType == ChatPeerType.Bot) MR.images.ic_cube
else MR.images.ic_account_circle_filled
)
}
},
information = ownerVerificationMessage(ownerVerification),
confirmText = generalGetString(MR.strings.connect_plan_open_new_chat),