Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: User verification status Proteus (WPB-1775) #2299

Merged
merged 13 commits into from
Oct 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class UIParticipantMapper @Inject constructor(
unavailable = unavailable,
isDeleted = (user is OtherUser && user.deleted),
botService = (user as? OtherUser)?.botService,
isDefederated = (user is OtherUser && user.defederated)
isDefederated = (user is OtherUser && user.defederated),
isProteusVerified = (user is OtherUser && user.isProteusVerified),
)
}

Expand All @@ -69,7 +70,8 @@ class UIParticipantMapper @Inject constructor(
unavailable = !userSummary.isUserDeleted && userSummary.userName.orEmpty().isEmpty(),
isDeleted = userSummary.isUserDeleted,
isSelf = isSelfUser,
isDefederated = false
isDefederated = false,
isProteusVerified = false
)
}

Expand All @@ -84,7 +86,8 @@ class UIParticipantMapper @Inject constructor(
isDeleted = userSummary.isUserDeleted,
isSelf = false,
readReceiptDate = date,
isDefederated = false
isDefederated = false,
isProteusVerified = false
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ class MigrationMapper @Inject constructor() {
availabilityStatus = mapUserAvailabilityStatus(scalaUserData.availability),
botService = botService,
deleted = scalaUserData.deleted,
defederated = false
defederated = false,
isProteusVerified = false
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
package com.wire.android.ui.authentication.devices

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
Expand All @@ -49,14 +47,14 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.wire.android.BuildConfig
import com.wire.android.R
import com.wire.android.ui.authentication.devices.model.Device
import com.wire.android.ui.authentication.devices.model.lastActiveDescription
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.button.WireSecondaryButton
import com.wire.android.ui.common.button.getMinTouchMargins
import com.wire.android.ui.common.button.wireSecondaryButtonColors
Expand Down Expand Up @@ -185,7 +183,7 @@ private fun DeviceItemTexts(
)
if (shouldShowVerifyLabel) {
Spacer(modifier = Modifier.width(MaterialTheme.wireDimensions.spacing8x))
VerifyLabel(device.isVerified, Modifier.wrapContentWidth())
if (device.isVerifiedProteus) ProteusVerifiedIcon(Modifier.wrapContentWidth())
}
}

Expand Down Expand Up @@ -249,35 +247,12 @@ private fun DeviceItemTexts(
)
}

@Composable
fun VerifyLabel(isVerified: Boolean, modifier: Modifier = Modifier) {
Box(
modifier = modifier.border(
width = MaterialTheme.wireDimensions.spacing1x,
shape = RoundedCornerShape(MaterialTheme.wireDimensions.spacing4x),
color = if (isVerified) MaterialTheme.wireColorScheme.primary else MaterialTheme.wireColorScheme.secondaryText,
)
) {
Text(
text = stringResource(id = if (isVerified) R.string.label_client_verified else R.string.label_client_unverified),
color = if (isVerified) MaterialTheme.wireColorScheme.primary else MaterialTheme.wireColorScheme.secondaryText,
style = MaterialTheme.wireTypography.label03,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.wrapContentWidth()
.padding(horizontal = MaterialTheme.wireDimensions.spacing4x, vertical = MaterialTheme.wireDimensions.spacing2x)
)
}
}

@PreviewMultipleThemes
@Composable
fun PreviewDeviceItem() {
WireTheme {
DeviceItem(
device = Device(name = UIText.DynamicString("name")),
device = Device(name = UIText.DynamicString("name"), isVerifiedProteus = true),
placeholder = false,
shouldShowVerifyLabel = true,
background = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ data class Device(
val registrationTime: String? = null,
val lastActiveInWholeWeeks: Int? = null,
val isValid: Boolean = true,
val isVerified: Boolean = false,
val isVerifiedProteus: Boolean = false,
val mlsPublicKeys: Map<String, String>? = null
) {
constructor(client: Client) : this(
Expand All @@ -47,7 +47,7 @@ data class Device(
registrationTime = client.registrationTime?.toIsoDateTimeString(),
lastActiveInWholeWeeks = client.lastActiveInWholeWeeks(),
isValid = client.isValid,
isVerified = client.isVerified,
isVerifiedProteus = client.isVerified,
mlsPublicKeys = client.mlsPublicKeys
)
}
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.common

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.wire.android.R

@Composable
fun ProteusVerifiedIcon(modifier: Modifier = Modifier) {
Image(
modifier = modifier.padding(start = dimensions().spacing4x),
painter = painterResource(id = R.drawable.ic_certificate_valid_proteus),
contentDescription = stringResource(R.string.label_client_verified)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@ fun WireCenterAlignedTopAppBar(
actions: @Composable RowScope.() -> Unit = {},
modifier: Modifier = Modifier,
bottomContent: @Composable ColumnScope.() -> Unit = {}
) {
WireCenterAlignedTopAppBar(
titleContent = {
WireTopAppBarTitle(
title = title,
style = titleStyle,
maxLines = maxLines
)
},
subtitleContent = subtitleContent,
onNavigationPressed = onNavigationPressed,
navigationIconType = navigationIconType,
elevation = elevation,
actions = actions,
modifier = modifier,
bottomContent = bottomContent
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WireCenterAlignedTopAppBar(
titleContent: @Composable ColumnScope.() -> Unit,
subtitleContent: @Composable ColumnScope.() -> Unit = {},
onNavigationPressed: () -> Unit = {},
navigationIconType: NavigationIconType? = NavigationIconType.Back,
elevation: Dp = MaterialTheme.wireDimensions.topBarShadowElevation,
actions: @Composable RowScope.() -> Unit = {},
modifier: Modifier = Modifier,
bottomContent: @Composable ColumnScope.() -> Unit = {}
) {
Surface(
modifier = modifier,
Expand All @@ -62,11 +92,7 @@ fun WireCenterAlignedTopAppBar(
CenterAlignedTopAppBar(
title = {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
WireTopAppBarTitle(
title = title,
style = titleStyle,
maxLines = maxLines
)
titleContent()
subtitleContent()
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.wire.android.R
import com.wire.android.model.Clickable
import com.wire.android.model.UserAvatarData
import com.wire.android.ui.common.ArrowRightIcon
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.RowItemTemplate
import com.wire.android.ui.common.UserBadge
import com.wire.android.ui.common.UserProfileAvatar
Expand Down Expand Up @@ -92,8 +93,9 @@ fun ConversationParticipantItem(
startPadding = dimensions().spacing6x,
isDeleted = uiParticipant.isDeleted
)
}

if (uiParticipant.isProteusVerified) ProteusVerifiedIcon()
}
},
subtitle = {
HighlightSubtitle(
Expand Down Expand Up @@ -125,7 +127,7 @@ fun ConversationParticipantItem(
@Composable
fun PreviewGroupConversationParticipantItem() {
ConversationParticipantItem(
UIParticipant(UserId("0", ""), "name", "handle", false, false, UserAvatarData(), Membership.Guest),
UIParticipant(UserId("0", ""), "name", "handle", false, false, UserAvatarData(), Membership.Guest, isProteusVerified = true),
clickable = Clickable(enabled = true) {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ data class UIParticipant(
val isDeleted: Boolean = false,
val readReceiptDate: Instant? = null,
val botService: BotService? = null,
val isDefederated: Boolean = false
val isDefederated: Boolean = false,
val isProteusVerified: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.wire.android.ui.authentication.devices.remove.RemoveDeviceDialog
import com.wire.android.ui.authentication.devices.remove.RemoveDeviceDialogState
import com.wire.android.ui.authentication.devices.remove.RemoveDeviceError
import com.wire.android.ui.common.CopyButton
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.WireDialog
import com.wire.android.ui.common.WireDialogButtonProperties
import com.wire.android.ui.common.WireDialogButtonType
Expand All @@ -52,6 +53,7 @@ import com.wire.android.ui.common.colorsScheme
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.wire.android.ui.common.topappbar.WireTopAppBarTitle
import com.wire.android.ui.home.conversationslist.common.FolderHeader
import com.wire.android.ui.settings.devices.model.DeviceDetailsState
import com.wire.android.ui.theme.wireColorScheme
Expand Down Expand Up @@ -102,13 +104,7 @@ fun DeviceDetailsContent(
) {
val screenState = rememberConversationScreenState()
WireScaffold(
topBar = {
WireCenterAlignedTopAppBar(
onNavigationPressed = onNavigateBack,
elevation = 0.dp,
title = state.device.name.asString()
)
},
topBar = { DeviceDetailsTopBar(onNavigateBack, state.device, state.isCurrentDevice) },
bottomBar = {
Column(
Modifier
Expand Down Expand Up @@ -195,7 +191,7 @@ fun DeviceDetailsContent(
if (!state.isCurrentDevice) {
item {
DeviceVerificationItem(
state.device.isVerified,
state.device.isVerifiedProteus,
state.fingerPrint != null,
state.isSelfClient,
state.userName,
Expand Down Expand Up @@ -231,6 +227,30 @@ fun DeviceDetailsContent(
}
}

@Composable
private fun DeviceDetailsTopBar(
onNavigateBack: () -> Unit,
device: Device,
isCurrentDevice: Boolean
) {
WireCenterAlignedTopAppBar(
onNavigationPressed = onNavigateBack,
elevation = 0.dp,
titleContent = {
Row {
WireTopAppBarTitle(
title = device.name.asString(),
style = MaterialTheme.wireTypography.title01,
maxLines = 2
)
if (!isCurrentDevice && device.isVerifiedProteus) {
ProteusVerifiedIcon()
}
}
}
)
}

@Composable
private fun DeviceIdItem(state: DeviceDetailsState, onCopy: (String) -> Unit) {
DeviceDetailSectionContent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.CircleShape
Expand All @@ -56,6 +56,7 @@ import com.wire.android.model.Clickable
import com.wire.android.model.ImageAsset.UserAvatarAsset
import com.wire.android.model.UserAvatarData
import com.wire.android.ui.common.Icon
import com.wire.android.ui.common.ProteusVerifiedIcon
import com.wire.android.ui.common.UserBadge
import com.wire.android.ui.common.UserProfileAvatar
import com.wire.android.ui.common.banner.SecurityClassificationBannerForUser
Expand Down Expand Up @@ -87,6 +88,7 @@ fun UserProfileInfo(
modifier: Modifier = Modifier,
connection: ConnectionState = ConnectionState.ACCEPTED,
delayToShowPlaceholderIfNoAsset: Duration = 200.milliseconds,
isProteusVerified: Boolean = false
) {
Column(
horizontalAlignment = CenterHorizontally,
Expand Down Expand Up @@ -163,16 +165,21 @@ fun UserProfileInfo(
end.linkTo(parent.end)
}
) {
Text(
text = fullName.ifBlank {
if (isLoading) ""
else UIText.StringResource(R.string.username_unavailable_label).asString()
},
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.wireTypography.title02,
color = if (fullName.isNotBlank()) MaterialTheme.colorScheme.onBackground else MaterialTheme.wireColorScheme.labelText
)
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = fullName.ifBlank {
if (isLoading) ""
else UIText.StringResource(R.string.username_unavailable_label).asString()
},
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.wireTypography.title02,
color = if (fullName.isNotBlank()) MaterialTheme.colorScheme.onBackground
else MaterialTheme.wireColorScheme.labelText
)

if (isProteusVerified) ProteusVerifiedIcon()
}
Text(
text = if (membership == Membership.Service) userName else userName.ifNotEmpty { "@$userName" },
overflow = TextOverflow.Ellipsis,
Expand Down Expand Up @@ -261,6 +268,7 @@ fun PreviewUserProfileInfo() {
fullName = "fullName",
onUserProfileClick = {},
teamName = "Wire",
connection = ConnectionState.ACCEPTED
connection = ConnectionState.ACCEPTED,
isProteusVerified = true
)
}