Skip to content

Commit

Permalink
feat: Show conversation Proteus verification status (#2350)
Browse files Browse the repository at this point in the history
Co-authored-by: Mojtaba Chenani <[email protected]>
  • Loading branch information
borichellow and mchenani authored Oct 27, 2023
1 parent a60a8f8 commit 3c608e9
Show file tree
Hide file tree
Showing 68 changed files with 344 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,14 @@ fun MessagePreview.uiLastMessageContent(): UILastMessageContent {
}

MessagePreviewContent.CryptoSessionReset -> UILastMessageContent.None
MessagePreviewContent.VerificationChanged.VerifiedMls ->
UILastMessageContent.VerificationChanged(R.string.last_message_verified_conversation_mls)
MessagePreviewContent.VerificationChanged.VerifiedProteus ->
UILastMessageContent.VerificationChanged(R.string.last_message_verified_conversation_proteus)
MessagePreviewContent.VerificationChanged.DegradedMls ->
UILastMessageContent.VerificationChanged(R.string.last_message_conversations_verification_degraded_mls)
MessagePreviewContent.VerificationChanged.DegradedProteus ->
UILastMessageContent.VerificationChanged(R.string.last_message_conversations_verification_degraded_proteus)
Unknown -> UILastMessageContent.None
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class MigrationMapper @Inject constructor() {
userMessageTimer = null,
archived = false,
archivedDateTime = null,
verificationStatus = Conversation.VerificationStatus.NOT_VERIFIED
mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ private fun DeviceItemTexts(
)
if (shouldShowVerifyLabel) {
Spacer(modifier = Modifier.width(MaterialTheme.wireDimensions.spacing8x))
if (device.isVerifiedProteus) ProteusVerifiedIcon(Modifier.wrapContentWidth())
if (device.isVerifiedProteus) ProteusVerifiedIcon(Modifier.wrapContentWidth().align(Alignment.CenterVertically))
}
}

Expand Down
20 changes: 18 additions & 2 deletions app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.wire.android.ui.common

import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
Expand All @@ -26,10 +27,25 @@ import androidx.compose.ui.res.stringResource
import com.wire.android.R

@Composable
fun ProteusVerifiedIcon(modifier: Modifier = Modifier) {
fun ProteusVerifiedIcon(
modifier: Modifier = Modifier,
@StringRes contentDescriptionId: Int = R.string.label_client_verified
) {
Image(
modifier = modifier.padding(start = dimensions().spacing4x),
painter = painterResource(id = R.drawable.ic_certificate_valid_proteus),
contentDescription = stringResource(R.string.label_client_verified)
contentDescription = stringResource(contentDescriptionId)
)
}

@Composable
fun MLSVerifiedIcon(
modifier: Modifier = Modifier,
@StringRes contentDescriptionId: Int = R.string.label_client_verified
) {
Image(
modifier = modifier.padding(start = dimensions().spacing4x),
painter = painterResource(id = R.drawable.ic_certificate_valid_mls),
contentDescription = stringResource(contentDescriptionId)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ data class ConversationSheetContent(
val conversationTypeDetail: ConversationTypeDetail,
val selfRole: Conversation.Member.Role?,
val isTeamConversation: Boolean,
val isArchived: Boolean
val isArchived: Boolean,
val protocol: Conversation.ProtocolInfo,
val mlsVerificationStatus: Conversation.VerificationStatus,
val proteusVerificationStatus: Conversation.VerificationStatus
) {

private val isSelfUserMember: Boolean get() = selfRole != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ fun rememberConversationSheetState(
),
isTeamConversation = teamId != null,
selfRole = selfMemberRole,
isArchived = conversationItem.isArchived
isArchived = conversationItem.isArchived,
protocol = Conversation.ProtocolInfo.Proteus,
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED
)
}
}

is ConversationItem.PrivateConversation -> {
with(conversationItem) {
ConversationSheetContent(
Expand All @@ -95,10 +99,14 @@ fun rememberConversationSheetState(
),
isTeamConversation = isTeamConversation,
selfRole = Conversation.Member.Role.Member,
isArchived = conversationItem.isArchived
isArchived = conversationItem.isArchived,
protocol = Conversation.ProtocolInfo.Proteus,
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED
)
}
}

is ConversationItem.ConnectionConversation -> {
with(conversationItem) {
ConversationSheetContent(
Expand All @@ -110,7 +118,10 @@ fun rememberConversationSheetState(
),
isTeamConversation = isTeamConversation,
selfRole = Conversation.Member.Role.Member,
isArchived = conversationItem.isArchived
isArchived = conversationItem.isArchived,
protocol = Conversation.ProtocolInfo.Proteus,
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@

package com.wire.android.ui.home.conversations

import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
Expand All @@ -48,6 +48,8 @@ import com.wire.android.R
import com.wire.android.model.UserAvatarData
import com.wire.android.ui.calling.controlbuttons.JoinButton
import com.wire.android.ui.calling.controlbuttons.StartCallButton
import com.wire.android.ui.common.MLSVerifiedIcon
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.UserProfileAvatar
import com.wire.android.ui.common.button.WireSecondaryIconButton
import com.wire.android.ui.common.colorsScheme
Expand Down Expand Up @@ -133,7 +135,11 @@ private fun ConversationScreenTopAppBarContent(
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(weight = 1f, fill = false)
)
VerificationIcon(conversationInfoViewState.protocolInfo, conversationInfoViewState.verificationStatus)
VerificationIcons(
conversationInfoViewState.protocolInfo,
conversationInfoViewState.mlsVerificationStatus,
conversationInfoViewState.proteusVerificationStatus
)
if (isDropDownEnabled && isInteractionEnabled) {
Icon(
painter = painterResource(id = R.drawable.ic_dropdown_icon),
Expand Down Expand Up @@ -181,21 +187,29 @@ private fun ConversationScreenTopAppBarContent(
}

@Composable
private fun VerificationIcon(protocolInfo: Conversation.ProtocolInfo?, verificationStatus: Conversation.VerificationStatus?) {
if (verificationStatus != Conversation.VerificationStatus.VERIFIED || protocolInfo == null) return

val (iconId, contentDescriptionId) = when (protocolInfo) {
is Conversation.ProtocolInfo.MLS ->
R.drawable.ic_certificate_valid_mls to R.string.content_description_mls_certificate_valid
private fun RowScope.VerificationIcons(
protocolInfo: Conversation.ProtocolInfo?,
mlsVerificationStatus: Conversation.VerificationStatus?,
proteusVerificationStatus: Conversation.VerificationStatus?
) {
val mlsIcon: @Composable () -> Unit = {
if (mlsVerificationStatus == Conversation.VerificationStatus.VERIFIED) {
MLSVerifiedIcon(contentDescriptionId = R.string.content_description_mls_certificate_valid)
}
}
val proteusIcon: @Composable () -> Unit = {
if (proteusVerificationStatus == Conversation.VerificationStatus.VERIFIED) {
ProteusVerifiedIcon(contentDescriptionId = R.string.content_description_proteus_certificate_valid)
}
}

is Conversation.ProtocolInfo.Proteus, is Conversation.ProtocolInfo.Mixed ->
R.drawable.ic_certificate_valid_proteus to R.string.content_description_proteus_certificate_valid
if (protocolInfo is Conversation.ProtocolInfo.Proteus) {
proteusIcon()
mlsIcon()
} else {
mlsIcon()
proteusIcon()
}
Image(
modifier = Modifier.padding(start = dimensions().spacing4x),
painter = painterResource(id = iconId),
contentDescription = stringResource(contentDescriptionId)
)
}

@Composable
Expand Down Expand Up @@ -371,3 +385,30 @@ fun PreviewConversationScreenTopAppBarShortTitleWithOngoingCall() {
isSearchEnabled = false
)
}

@Preview("Topbar with a short conversation title and verified")
@Composable
fun PreviewConversationScreenTopAppBarShortTitleWithVerified() {
val conversationId = QualifiedID("", "")
ConversationScreenTopAppBarContent(
ConversationInfoViewState(
conversationId = ConversationId("value", "domain"),
conversationName = UIText.DynamicString("Short title"),
conversationDetailsData = ConversationDetailsData.Group(conversationId),
conversationAvatar = ConversationAvatar.Group(conversationId),
protocolInfo = Conversation.ProtocolInfo.Proteus,
proteusVerificationStatus = Conversation.VerificationStatus.VERIFIED,
mlsVerificationStatus = Conversation.VerificationStatus.VERIFIED
),
onBackButtonClick = {},
onDropDownClick = {},
isDropDownEnabled = true,
onSearchButtonClick = {},
onPhoneButtonClick = {},
hasOngoingCall = false,
onJoinCallButtonClick = {},
onPermanentPermissionDecline = {},
isInteractionEnabled = true,
isSearchEnabled = false
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
Expand All @@ -35,6 +38,7 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -47,10 +51,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.annotation.Destination
Expand All @@ -64,7 +70,9 @@ import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
import com.wire.android.navigation.style.PopUpNavigationAnimation
import com.wire.android.ui.common.CollapsingTopBarScaffold
import com.wire.android.ui.common.MLSVerifiedIcon
import com.wire.android.ui.common.MoreOptionIcon
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.TabItem
import com.wire.android.ui.common.WireTabRow
import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout
Expand All @@ -73,17 +81,20 @@ import com.wire.android.ui.common.bottomsheet.conversation.rememberConversationS
import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState
import com.wire.android.ui.common.calculateCurrentTab
import com.wire.android.ui.common.dialogs.ArchiveConversationDialog
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.snackbar.LocalSnackbarHostState
import com.wire.android.ui.common.topBarElevation
import com.wire.android.ui.common.topappbar.NavigationIconType
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.wire.android.ui.common.topappbar.WireTopAppBarTitle
import com.wire.android.ui.common.visbility.rememberVisibilityState
import com.wire.android.ui.destinations.AddMembersSearchScreenDestination
import com.wire.android.ui.destinations.EditConversationNameScreenDestination
import com.wire.android.ui.destinations.EditGuestAccessScreenDestination
import com.wire.android.ui.destinations.EditSelfDeletingMessagesScreenDestination
import com.wire.android.ui.destinations.GroupConversationAllParticipantsScreenDestination
import com.wire.android.ui.destinations.OtherUserProfileScreenDestination
import com.wire.android.ui.destinations.SearchConversationMessagesScreenDestination
import com.wire.android.ui.destinations.SelfUserProfileScreenDestination
import com.wire.android.ui.destinations.ServiceDetailsScreenDestination
import com.wire.android.ui.home.conversations.details.dialog.ClearConversationContentDialog
Expand All @@ -97,11 +108,12 @@ import com.wire.android.ui.home.conversations.details.participants.GroupConversa
import com.wire.android.ui.home.conversations.details.participants.model.UIParticipant
import com.wire.android.ui.home.conversationslist.model.DialogState
import com.wire.android.ui.home.conversationslist.model.GroupDialogState
import com.wire.android.ui.destinations.SearchConversationMessagesScreenDestination
import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.ui.theme.wireDimensions
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.ui.UIText
import com.wire.kalium.logic.data.conversation.Conversation
import kotlinx.coroutines.launch

@RootNavGraph
Expand Down Expand Up @@ -303,7 +315,14 @@ private fun GroupConversationDetailsContent(
topBarHeader = {
WireCenterAlignedTopAppBar(
elevation = elevationState,
title = stringResource(R.string.conversation_details_title),
titleContent = {
WireTopAppBarTitle(
title = stringResource(R.string.conversation_details_title),
style = MaterialTheme.wireTypography.title01,
maxLines = 2
)
VerificationInfo(conversationSheetContent)
},
navigationIconType = NavigationIconType.Close,
onNavigationPressed = onBackPressed,
actions = { MoreOptionIcon(onButtonClicked = openBottomSheet) }
Expand Down Expand Up @@ -446,6 +465,59 @@ private fun GroupConversationDetailsContent(
)
}

@Composable
private fun VerificationInfo(conversationSheetContent: ConversationSheetContent?) {
if (conversationSheetContent == null) return

val isProteusVerified = conversationSheetContent.proteusVerificationStatus == Conversation.VerificationStatus.VERIFIED
val isMlsVerified = conversationSheetContent.mlsVerificationStatus == Conversation.VerificationStatus.VERIFIED
val isProteusProtocol = conversationSheetContent.protocol == Conversation.ProtocolInfo.Proteus

if (isProteusVerified && (isProteusProtocol || !isMlsVerified)) {
ProteusVerifiedLabel()
} else if (isMlsVerified) {
MLSVerifiedLabel()
}
}

@Composable
private fun MLSVerifiedLabel() {
VerifiedLabel(
stringResource(id = R.string.label_conversations_details_verified_mls).uppercase(),
MaterialTheme.wireColorScheme.mlsVerificationTextColor
) { MLSVerifiedIcon() }
}

@Composable
private fun ProteusVerifiedLabel() {
VerifiedLabel(
stringResource(id = R.string.label_conversations_details_verified_proteus).uppercase(),
MaterialTheme.wireColorScheme.primary
) { ProteusVerifiedIcon() }
}

@Composable
private fun VerifiedLabel(text: String, color: Color, icon: @Composable RowScope.() -> Unit = {}) {
Row(
modifier = Modifier
.padding(top = dimensions().spacing4x)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier.padding(
start = dimensions().spacing6x,
end = dimensions().spacing6x
),
text = text,
style = MaterialTheme.wireTypography.label01,
color = color,
overflow = TextOverflow.Ellipsis
)
icon()
}
}

enum class GroupConversationDetailsTabItem(@StringRes override val titleResId: Int) : TabItem {
OPTIONS(R.string.conversation_details_options_tab),
PARTICIPANTS(R.string.conversation_details_participants_tab);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ class GroupConversationDetailsViewModel @Inject constructor(
conversationTypeDetail = ConversationTypeDetail.Group(conversationId, groupDetails.isSelfUserCreator),
isTeamConversation = groupDetails.conversation.teamId?.value != null,
selfRole = groupDetails.selfRole,
isArchived = groupDetails.conversation.archived
isArchived = groupDetails.conversation.archived,
protocol = groupDetails.conversation.protocol,
mlsVerificationStatus = groupDetails.conversation.mlsVerificationStatus,
proteusVerificationStatus = groupDetails.conversation.proteusVerificationStatus
)
val isGuestAllowed = groupDetails.conversation.isGuestAllowed() || groupDetails.conversation.isNonTeamMemberAllowed()
val isUpdatingReadReceiptAllowed = if (selfTeam == null) {
Expand Down
Loading

0 comments on commit 3c608e9

Please sign in to comment.