From 45b18943a2b8719622f55dd8b7523a6b14005394 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 18 Jul 2024 09:21:27 +0200 Subject: [PATCH 1/7] chore(mls): unify MLSClientIdentity models (WPB-9774) (#2818) * chore: refactor identity models * fix tests * user correct clientId and Handle in MLSClientIdentity object * clean mapping object checker * fix formatting and remove one line un-used code --------- Co-authored-by: Vitor Hugo Schwaab (cherry picked from commit 8f000c04316f32576e5a95813053c265e1143fa2) --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index afefda751f2..3acfed05ca2 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit afefda751f20ddbe9a007c36a1d22bf72a15fd63 +Subproject commit 3acfed05ca26bc52841ad9424692ebf225afd052 From eafa49fd05573f0c3fc1436c441384e8f5227609 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 18 Jul 2024 14:38:26 +0200 Subject: [PATCH 2/7] chore(mls): unify MLSClientIdentity models (WPB-9774) (#2818) * chore: refactor identity models * fix tests * user correct clientId and Handle in MLSClientIdentity object * clean mapping object checker * fix formatting and remove one line un-used code --------- Co-authored-by: Vitor Hugo Schwaab (cherry picked from commit 8f000c04316f32576e5a95813053c265e1143fa2) --- .../android/di/accountScoped/UserModule.kt | 8 +- .../android/mapper/UIParticipantMapper.kt | 9 +-- .../ui/authentication/devices/DeviceItem.kt | 6 +- .../ui/authentication/devices/model/Device.kt | 14 ++-- .../wire/android/ui/common/VerifiedIcons.kt | 13 +-- ...serveParticipantsForConversationUseCase.kt | 9 ++- .../settings/devices/DeviceDetailsScreen.kt | 47 ++++++----- .../devices/DeviceDetailsViewModel.kt | 18 ++--- .../EndToEndIdentityCertificateItem.kt | 80 ++++++++++++------- .../e2ei/E2eiCertificateDetailsBottomSheet.kt | 8 +- .../E2eiCertificateDetailsScreenNavArgs.kt | 17 +++- .../e2ei/E2eiCertificateDetailsViewModel.kt | 22 ++++- .../devices/model/DeviceDetailsState.kt | 13 +-- .../other/OtherUserDevicesScreen.kt | 2 +- .../other/OtherUserProfileScreenViewModel.kt | 10 +-- ...eParticipantsForConversationUseCaseTest.kt | 6 +- .../devices/DeviceDetailsViewModelTest.kt | 58 +++++++++----- .../OtherUserProfileViewModelArrangement.kt | 8 +- 18 files changed, 209 insertions(+), 139 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/UserModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/UserModule.kt index 524bbfc748f..3fa653b074f 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/UserModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/UserModule.kt @@ -27,9 +27,9 @@ import com.wire.kalium.logic.feature.asset.GetAvatarAssetUseCase import com.wire.kalium.logic.feature.client.FinalizeMLSClientAfterE2EIEnrollment import com.wire.kalium.logic.feature.conversation.GetAllContactsNotInConversationUseCase import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorker -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCase -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase import com.wire.kalium.logic.feature.e2ei.usecase.ObserveCertificateRevocationForSelfClientUseCase import com.wire.kalium.logic.feature.featureConfig.FeatureFlagsSyncWorker @@ -212,12 +212,12 @@ class UserModule { @ViewModelScoped @Provides - fun provideGetE2EICertificateUseCase(userScope: UserScope): GetE2eiCertificateUseCase = + fun provideGetE2EICertificateUseCase(userScope: UserScope): GetMLSClientIdentityUseCase = userScope.getE2EICertificate @ViewModelScoped @Provides - fun provideGetUserE2eiCertificateStatusUseCase(userScope: UserScope): GetUserE2eiCertificateStatusUseCase = + fun provideGetUserE2eiCertificateStatusUseCase(userScope: UserScope): IsOtherUserE2EIVerifiedUseCase = userScope.getUserE2eiCertificateStatus @ViewModelScoped diff --git a/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt index ae99a2256ee..85736024d46 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt @@ -29,18 +29,13 @@ import com.wire.kalium.logic.data.user.OtherUser import com.wire.kalium.logic.data.user.SelfUser import com.wire.kalium.logic.data.user.User import com.wire.kalium.logic.data.user.type.UserType -import com.wire.kalium.logic.feature.e2ei.CertificateStatus import javax.inject.Inject class UIParticipantMapper @Inject constructor( private val userTypeMapper: UserTypeMapper, private val wireSessionImageLoader: WireSessionImageLoader ) { - fun toUIParticipant( - user: User, - mlsCertificateStatus: CertificateStatus? = null, - isUnderLegalHold: Boolean = false, - ): UIParticipant = with(user) { + fun toUIParticipant(user: User, isMLSVerified: Boolean = false): UIParticipant = with(user) { val (userType, connectionState, unavailable) = when (this) { is OtherUser -> Triple(this.userType, this.connectionStatus, this.isUnavailableUser) // TODO(refactor): does self user need a type ? to false @@ -60,7 +55,7 @@ class UIParticipantMapper @Inject constructor( botService = (user as? OtherUser)?.botService, isDefederated = (user is OtherUser && user.defederated), isProteusVerified = (user is OtherUser && user.isProteusVerified), - isMLSVerified = mlsCertificateStatus == CertificateStatus.VALID, + isMLSVerified = isMLSVerified, supportedProtocolList = supportedProtocols.orEmpty().toList(), isUnderLegalHold = isUnderLegalHold, ) diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt index dcbe53947cb..8a23a1b86b7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt @@ -193,7 +193,7 @@ private fun ColumnScope.DeviceItemTexts( ) if (shouldShowVerifyLabel) { if (shouldShowE2EIInfo) { - MLSVerificationIcon(device.e2eiCertificate?.status) + MLSVerificationIcon(device.mlsClientIdentity?.e2eiStatus) } if (device.isVerifiedProteus && !isCurrentClient) { ProteusVerifiedIcon( @@ -216,7 +216,7 @@ private fun ColumnScope.DeviceItemTexts( Spacer(modifier = Modifier.height(MaterialTheme.wireDimensions.removeDeviceItemTitleVerticalPadding)) - device.e2eiCertificate?.let { certificate -> + device.mlsClientIdentity?.let { identity -> Text( style = MaterialTheme.wireTypography.subline01, color = MaterialTheme.wireColorScheme.labelText, @@ -224,7 +224,7 @@ private fun ColumnScope.DeviceItemTexts( overflow = TextOverflow.Ellipsis, text = stringResource( R.string.remove_device_mls_thumbprint_label, - certificate.thumbprint.formatAsFingerPrint() + identity.thumbprint.formatAsFingerPrint() ), modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/model/Device.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/model/Device.kt index 435d8443803..0f271364d51 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/model/Device.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/model/Device.kt @@ -26,7 +26,7 @@ import com.wire.android.R import com.wire.android.util.ui.UIText import com.wire.kalium.logic.data.client.Client import com.wire.kalium.logic.data.conversation.ClientId -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity import com.wire.kalium.logic.util.inWholeWeeks import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString import kotlinx.datetime.Clock @@ -38,16 +38,16 @@ data class Device( val lastActiveInWholeWeeks: Int? = null, val isValid: Boolean = true, val isVerifiedProteus: Boolean = false, - val e2eiCertificate: E2eiCertificate? = null + val mlsClientIdentity: MLSClientIdentity? = null ) { - constructor(client: Client, e2eiCertificate: E2eiCertificate? = null) : this( + constructor(client: Client, mlsClientIdentity: MLSClientIdentity? = null) : this( name = client.displayName(), clientId = client.id, registrationTime = client.registrationTime?.toIsoDateTimeString(), lastActiveInWholeWeeks = client.lastActiveInWholeWeeks(), isValid = client.isValid, isVerifiedProteus = client.isVerified, - e2eiCertificate = e2eiCertificate + mlsClientIdentity = mlsClientIdentity ) fun updateFromClient(client: Client): Device = copy( @@ -57,11 +57,11 @@ data class Device( lastActiveInWholeWeeks = client.lastActiveInWholeWeeks(), isValid = client.isValid, isVerifiedProteus = client.isVerified, - e2eiCertificate = null, + mlsClientIdentity = null, ) - fun updateE2EICertificate(e2eiCertificate: E2eiCertificate): Device = copy( - e2eiCertificate = e2eiCertificate + fun updateE2EICertificate(mlsClientIdentity: MLSClientIdentity): Device = copy( + mlsClientIdentity = mlsClientIdentity ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt b/app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt index 3aeae7da9a0..40159d83bf8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/VerifiedIcons.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.wire.android.R import com.wire.kalium.logic.data.conversation.Conversation -import com.wire.kalium.logic.feature.e2ei.CertificateStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus @Composable fun RowScope.ConversationVerificationIcons( @@ -68,24 +68,27 @@ fun RowScope.ConversationVerificationIcons( } @Composable -fun RowScope.MLSVerificationIcon(mlsVerificationStatus: CertificateStatus?) { +fun RowScope.MLSVerificationIcon(mlsVerificationStatus: MLSClientE2EIStatus?) { val mlsIconModifier = Modifier .wrapContentWidth() .align(Alignment.CenterVertically) when (mlsVerificationStatus) { - CertificateStatus.VALID -> MLSVerifiedIcon( + MLSClientE2EIStatus.VALID -> MLSVerifiedIcon( contentDescriptionId = R.string.e2ei_certificat_status_valid, modifier = mlsIconModifier ) - CertificateStatus.REVOKED -> MLSRevokedIcon(modifier = mlsIconModifier) + MLSClientE2EIStatus.REVOKED -> MLSRevokedIcon(modifier = mlsIconModifier) - CertificateStatus.EXPIRED -> MLSNotVerifiedIcon( + MLSClientE2EIStatus.EXPIRED + -> MLSNotVerifiedIcon( contentDescriptionId = R.string.e2ei_certificat_status_expired, modifier = mlsIconModifier ) + MLSClientE2EIStatus.NOT_ACTIVATED -> MLSNotVerifiedIcon(modifier = mlsIconModifier) + null -> MLSNotVerifiedIcon(modifier = mlsIconModifier) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt index ead17b8ebe4..57bc5aeafad 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt @@ -31,7 +31,6 @@ import com.wire.kalium.logic.data.user.SelfUser import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase -import com.wire.kalium.logic.feature.e2ei.CertificateStatus import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCase import com.wire.kalium.logic.feature.legalhold.MembersHavingLegalHoldClientUseCase import com.wire.kalium.logic.functional.getOrElse @@ -59,7 +58,7 @@ class ObserveParticipantsForConversationUseCase @Inject constructor( } } .scan( - ConversationParticipantsData() to emptyMap() + ConversationParticipantsData() to emptyMap() ) { (_, previousMlsVerificationMap), sortedMemberList -> val allAdminsWithoutServices = sortedMemberList.getOrDefault(true, listOf()) val visibleAdminsWithoutServices = allAdminsWithoutServices.limit(limit) @@ -86,8 +85,10 @@ class ObserveParticipantsForConversationUseCase @Inject constructor( val selfUser = (allParticipants + allAdminsWithoutServices).firstOrNull { it.user is SelfUser } ConversationParticipantsData( - admins = visibleAdminsWithoutServices.toUIParticipants(), - participants = visibleParticipants.toUIParticipants(), + admins = visibleAdminsWithoutServices + .map { uiParticipantMapper.toUIParticipant(it.user, mlsVerificationMap[it.user.id].let { false }) }, + participants = visibleParticipants + .map { uiParticipantMapper.toUIParticipant(it.user, mlsVerificationMap[it.user.id].let { false }) }, allAdminsCount = allAdminsWithoutServices.size, allParticipantsCount = allParticipants.size, isSelfAnAdmin = allAdminsWithoutServices.any { it.user is SelfUser }, diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt index 399c4572b84..9051100e785 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt @@ -90,8 +90,13 @@ import com.wire.android.util.ui.PreviewMultipleThemes import com.wire.android.util.ui.UIText import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.conversation.ClientId -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate +import com.wire.kalium.logic.data.id.QualifiedClientID +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.e2ei.Handle +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.feature.e2ei.MLSCredentialsType +import com.wire.kalium.logic.feature.e2ei.X509Identity import com.wire.kalium.logic.feature.e2ei.usecase.E2EIEnrollmentResult import com.wire.kalium.logic.functional.Either import kotlinx.datetime.Instant @@ -138,7 +143,7 @@ fun DeviceDetailsContent( modifier: Modifier = Modifier, onDeleteDevice: () -> Unit = {}, onNavigateBack: () -> Unit = {}, - onNavigateToE2eiCertificateDetailsScreen: (String) -> Unit = {}, + onNavigateToE2eiCertificateDetailsScreen: (MLSClientIdentity) -> Unit = {}, onRemoveConfirm: () -> Unit = {}, onDialogDismiss: () -> Unit = {}, onErrorDialogDismiss: () -> Unit = {}, @@ -190,10 +195,9 @@ fun DeviceDetailsContent( .padding(internalPadding) .background(MaterialTheme.wireColorScheme.surface) ) { - - state.device.e2eiCertificate?.let { certificate -> + state.device.mlsClientIdentity?.let { identity -> item { - DeviceMLSSignatureItem(certificate.thumbprint, screenState::copyMessage) + DeviceMLSSignatureItem(identity.thumbprint, screenState::copyMessage) HorizontalDivider(color = MaterialTheme.wireColorScheme.background) } } @@ -202,7 +206,7 @@ fun DeviceDetailsContent( item { EndToEndIdentityCertificateItem( isE2eiCertificateActivated = state.isE2eiCertificateActivated, - certificate = state.e2eiCertificate, + mlsClientIdentity = state.mlsClientIdentity, isCurrentDevice = state.isCurrentDevice, isLoadingCertificate = state.isLoadingCertificate, enrollE2eiCertificate = { enrollE2eiCertificate() }, @@ -292,9 +296,9 @@ fun DeviceDetailsContent( ) } - if (state.isE2EICertificateEnrollSuccess) { + if (state.isE2EICertificateEnrollSuccess && state.mlsClientIdentity != null) { E2EISuccessDialog( - openCertificateDetails = { onNavigateToE2eiCertificateDetailsScreen(state.e2eiCertificate.certificateDetail) }, + openCertificateDetails = { onNavigateToE2eiCertificateDetailsScreen(state.mlsClientIdentity) }, dismissDialog = onEnrollE2EISuccessDismiss ) } @@ -327,7 +331,7 @@ private fun DeviceDetailsTopBar( ) if (shouldShowE2EIInfo) { - MLSVerificationIcon(device.e2eiCertificate?.status) + MLSVerificationIcon(device.mlsClientIdentity?.e2eiStatus) } if (!isCurrentDevice && device.isVerifiedProteus) { @@ -584,14 +588,21 @@ fun PreviewDeviceDetailsScreen() = WireTheme { clientId = ClientId(""), name = UIText.DynamicString("My Device"), registrationTime = "2022-03-24T18:02:30.360Z", - e2eiCertificate = E2eiCertificate( - "handler", - CertificateStatus.VALID, - "serial", - "Details", - "Thumbprint", - Instant.DISTANT_FUTURE - ) + mlsClientIdentity = MLSClientIdentity( + clientId = QualifiedClientID(ClientId(""), UserId("", "")), + e2eiStatus = MLSClientE2EIStatus.VALID, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.X509, + x509Identity = X509Identity( + handle = Handle("", "", ""), + displayName = "", + domain = "", + certificate = "", + serialNumber = "e5:d5:e6:75:7e:04:86:07:14:3c:a0:ed:9a:8d:e4:fd", + notBefore = Instant.DISTANT_PAST, + notAfter = Instant.DISTANT_FUTURE + ) + ), ), isCurrentDevice = false ), diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt index 6be29b071d8..1e9b5895a98 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt @@ -46,8 +46,7 @@ import com.wire.kalium.logic.feature.client.ObserveClientDetailsUseCase import com.wire.kalium.logic.feature.client.Result import com.wire.kalium.logic.feature.client.UpdateClientVerificationStatusUseCase import com.wire.kalium.logic.feature.e2ei.usecase.E2EIEnrollmentResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2EICertificateUseCaseResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase import com.wire.kalium.logic.feature.user.GetUserInfoResult import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase import com.wire.kalium.logic.feature.user.IsPasswordRequiredUseCase @@ -72,7 +71,7 @@ class DeviceDetailsViewModel @Inject constructor( private val fingerprintUseCase: ClientFingerprintUseCase, private val updateClientVerificationStatus: UpdateClientVerificationStatusUseCase, private val observeUserInfo: ObserveUserInfoUseCase, - private val e2eiCertificate: GetE2eiCertificateUseCase, + private val e2eiCertificate: GetMLSClientIdentityUseCase, isE2EIEnabledUseCase: IsE2EIEnabledUseCase ) : SavedStateViewModel(savedStateHandle) { @@ -132,17 +131,16 @@ class DeviceDetailsViewModel @Inject constructor( private fun getE2eiCertificate() { viewModelScope.launch { - val certificate = e2eiCertificate(deviceId) - state = if (certificate is GetE2EICertificateUseCaseResult.Success) { + state = e2eiCertificate(deviceId).fold({ + state.copy(isE2eiCertificateActivated = false, isLoadingCertificate = false) + }, { mlsClientIdentity -> state.copy( isE2eiCertificateActivated = true, - e2eiCertificate = certificate.certificate, + mlsClientIdentity = mlsClientIdentity, isLoadingCertificate = false, - device = state.device.updateE2EICertificate(certificate.certificate) + device = state.device.updateE2EICertificate(mlsClientIdentity) ) - } else { - state.copy(isE2eiCertificateActivated = false, isLoadingCertificate = false) - } + }) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/EndToEndIdentityCertificateItem.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/EndToEndIdentityCertificateItem.kt index f1d8baa5486..a8565e7af57 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/EndToEndIdentityCertificateItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/EndToEndIdentityCertificateItem.kt @@ -40,18 +40,24 @@ 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.PreviewMultipleThemes -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.id.QualifiedClientID +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.e2ei.Handle +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.feature.e2ei.MLSCredentialsType +import com.wire.kalium.logic.feature.e2ei.X509Identity import kotlinx.datetime.Instant @Composable fun EndToEndIdentityCertificateItem( isE2eiCertificateActivated: Boolean, - certificate: E2eiCertificate?, + mlsClientIdentity: MLSClientIdentity?, isCurrentDevice: Boolean, isLoadingCertificate: Boolean, enrollE2eiCertificate: () -> Unit, - showCertificate: (String) -> Unit + showCertificate: (MLSClientIdentity) -> Unit ) { Column( modifier = Modifier @@ -77,24 +83,24 @@ fun EndToEndIdentityCertificateItem( color = MaterialTheme.wireColorScheme.secondaryText, ) Column { - if (isE2eiCertificateActivated && certificate != null) { - when (certificate.status) { - CertificateStatus.REVOKED -> { + if (isE2eiCertificateActivated && mlsClientIdentity != null && mlsClientIdentity.credentialType == MLSCredentialsType.X509) { + when (mlsClientIdentity.e2eiStatus) { + MLSClientE2EIStatus.REVOKED -> { E2EIStatusRow( label = stringResource(id = R.string.e2ei_certificat_status_revoked), labelColor = colorsScheme().error, icon = R.drawable.ic_certificate_revoked_mls ) - SerialNumberBlock(certificate.serialNumber) + SerialNumberBlock(mlsClientIdentity.x509Identity!!.serialNumber) } - CertificateStatus.EXPIRED -> { + MLSClientE2EIStatus.EXPIRED -> { E2EIStatusRow( label = stringResource(id = R.string.e2ei_certificat_status_expired), labelColor = colorsScheme().error, icon = R.drawable.ic_certificate_not_activated_mls ) - SerialNumberBlock(certificate.serialNumber) + SerialNumberBlock(mlsClientIdentity.x509Identity!!.serialNumber) if (isCurrentDevice) { UpdateE2eiCertificateButton( enabled = true, @@ -104,13 +110,13 @@ fun EndToEndIdentityCertificateItem( } } - CertificateStatus.VALID -> { + MLSClientE2EIStatus.VALID -> { E2EIStatusRow( label = stringResource(id = R.string.e2ei_certificat_status_valid), labelColor = colorsScheme().validE2eiStatusColor, icon = R.drawable.ic_certificate_valid_mls ) - SerialNumberBlock(certificate.serialNumber) + SerialNumberBlock(mlsClientIdentity.x509Identity!!.serialNumber) if (isCurrentDevice) { UpdateE2eiCertificateButton( enabled = true, @@ -119,12 +125,26 @@ fun EndToEndIdentityCertificateItem( ) } } + MLSClientE2EIStatus.NOT_ACTIVATED -> { + E2EIStatusRow( + label = stringResource(id = R.string.e2ei_certificat_status_not_activated), + labelColor = colorsScheme().error, + icon = R.drawable.ic_certificate_not_activated_mls + ) + if (isCurrentDevice) { + GetE2eiCertificateButton( + enabled = true, + isLoading = isLoadingCertificate, + onGetCertificateClicked = enrollE2eiCertificate + ) + } + } } ShowE2eiCertificateButton( enabled = true, isLoading = false, onShowCertificateClicked = { - showCertificate(certificate.certificateDetail) + showCertificate(mlsClientIdentity) } ) } else { @@ -198,14 +218,7 @@ fun PreviewEndToEndIdentityCertificateItem() { EndToEndIdentityCertificateItem( isE2eiCertificateActivated = true, isCurrentDevice = false, - certificate = E2eiCertificate( - userHandle = "user_handle", - status = CertificateStatus.VALID, - serialNumber = "e5:d5:e6:75:7e:04:86:07:14:3c:a0:ed:9a:8d:e4:fd", - certificateDetail = "", - thumbprint = "thumbPrint", - endAt = Instant.DISTANT_FUTURE - ), + mlsClientIdentity = previewMLSClientIdentity(), isLoadingCertificate = false, enrollE2eiCertificate = {}, showCertificate = {} @@ -218,16 +231,25 @@ fun PreviewEndToEndIdentityCertificateSelfItem() { EndToEndIdentityCertificateItem( isE2eiCertificateActivated = true, isCurrentDevice = true, - certificate = E2eiCertificate( - userHandle = "user_handle", - status = CertificateStatus.VALID, - serialNumber = "e5:d5:e6:75:7e:04:86:07:14:3c:a0:ed:9a:8d:e4:fd", - certificateDetail = "", - thumbprint = "thumbPrint", - endAt = Instant.DISTANT_FUTURE - ), + mlsClientIdentity = previewMLSClientIdentity(), isLoadingCertificate = false, enrollE2eiCertificate = {}, showCertificate = {} ) } + +internal fun previewMLSClientIdentity() = MLSClientIdentity( + clientId = QualifiedClientID(ClientId(""), UserId("", "")), + e2eiStatus = MLSClientE2EIStatus.VALID, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.X509, + x509Identity = X509Identity( + handle = Handle("", "", ""), + displayName = "", + domain = "", + certificate = "", + serialNumber = "e5:d5:e6:75:7e:04:86:07:14:3c:a0:ed:9a:8d:e4:fd", + notBefore = Instant.DISTANT_PAST, + notAfter = Instant.DISTANT_FUTURE + ) +) diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt index 506723fd61c..5ccd660cb55 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt @@ -28,6 +28,7 @@ import com.wire.android.ui.common.bottomsheet.MenuModalSheetContent import com.wire.android.ui.common.bottomsheet.MenuModalSheetHeader import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout import com.wire.android.ui.common.bottomsheet.WireModalSheetState +import com.wire.android.util.permission.rememberWriteStorageRequestFlow @Composable fun E2eiCertificateDetailsBottomSheet( @@ -36,7 +37,10 @@ fun E2eiCertificateDetailsBottomSheet( onDownload: () -> Unit, ) { val coroutineScope = rememberCoroutineScope() - + val onSaveFileWriteStorageRequest = rememberWriteStorageRequestFlow( + onGranted = onDownload, + onDenied = { } + ) WireModalSheetLayout(sheetState = sheetState, coroutineScope = coroutineScope) { MenuModalSheetContent( header = MenuModalSheetHeader.Gone, @@ -53,7 +57,7 @@ fun E2eiCertificateDetailsBottomSheet( CreateCertificateSheetItem( title = stringResource(R.string.e2ei_certificate_details_download), icon = R.drawable.ic_download, - onClicked = onDownload, + onClicked = onSaveFileWriteStorageRequest::launch, enabled = true ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsScreenNavArgs.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsScreenNavArgs.kt index 340caeb21de..4d2a36e4f88 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsScreenNavArgs.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsScreenNavArgs.kt @@ -17,6 +17,17 @@ */ package com.wire.android.ui.settings.devices.e2ei -data class E2eiCertificateDetailsScreenNavArgs( - val certificateString: String -) +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +data class E2eiCertificateDetailsScreenNavArgs(val certificateDetails: E2EICertificateDetails) + +@Serializable +sealed class E2EICertificateDetails { + @Serializable + data class AfterLoginCertificateDetails(@SerialName("certificate") val mlsClientIdentity: MLSClientIdentity) : E2EICertificateDetails() + + @Serializable + data class DuringLoginCertificateDetails(@SerialName("certificate") val certificate: String) : E2EICertificateDetails() +} diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt index 3d195b8b38b..6d554fd0872 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt @@ -42,7 +42,7 @@ class E2eiCertificateDetailsViewModel @Inject constructor( var state: E2eiCertificateDetailsState by mutableStateOf(E2eiCertificateDetailsState()) private set - private val e2eiCertificateDetailsScreenNavArgs: E2eiCertificateDetailsScreenNavArgs = + private val navArgs: E2eiCertificateDetailsScreenNavArgs = savedStateHandle.navArgs() private var selfUserHandle: String? = null @@ -57,11 +57,27 @@ class E2eiCertificateDetailsViewModel @Inject constructor( } } - fun getCertificate() = e2eiCertificateDetailsScreenNavArgs.certificateString + fun getCertificate() = + when (navArgs.certificateDetails) { + is E2EICertificateDetails.DuringLoginCertificateDetails -> + navArgs.certificateDetails.certificate + + is E2EICertificateDetails.AfterLoginCertificateDetails -> + navArgs.certificateDetails.mlsClientIdentity.x509Identity?.certificate ?: "" + } + + fun userHandle() = + when (navArgs.certificateDetails) { + is E2EICertificateDetails.DuringLoginCertificateDetails -> + selfUserHandle + + is E2EICertificateDetails.AfterLoginCertificateDetails -> + navArgs.certificateDetails.mlsClientIdentity.x509Identity?.handle?.handle ?: "" + } fun getCertificateName(): String { val date = DateTimeUtil.currentInstant().fileDateTime() - return "wire-certificate-$selfUserHandle-$date.txt" + return "wire-certificate-${userHandle()}-$date.txt" } } diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt index f19965a0e85..b68745619e9 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt @@ -20,9 +20,7 @@ package com.wire.android.ui.settings.devices.model import com.wire.android.ui.authentication.devices.model.Device import com.wire.android.ui.authentication.devices.remove.RemoveDeviceDialogState import com.wire.android.ui.authentication.devices.remove.RemoveDeviceError -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate -import kotlinx.datetime.Instant +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity data class DeviceDetailsState( val device: Device = Device(), @@ -33,14 +31,7 @@ data class DeviceDetailsState( val isSelfClient: Boolean = false, val userName: String? = null, val isE2eiCertificateActivated: Boolean = false, - val e2eiCertificate: E2eiCertificate = E2eiCertificate( - userHandle = "", - status = CertificateStatus.EXPIRED, - serialNumber = "", - certificateDetail = "", - thumbprint = "", - endAt = Instant.DISTANT_FUTURE - ), + val mlsClientIdentity: MLSClientIdentity? = null, val canBeRemoved: Boolean = false, val isLoadingCertificate: Boolean = false, val isE2EICertificateEnrollSuccess: Boolean = false, diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserDevicesScreen.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserDevicesScreen.kt index 3b600ae09e9..4c1b655bf24 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserDevicesScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserDevicesScreen.kt @@ -121,7 +121,7 @@ private fun OtherUserDevicesContent( onClickAction = onDeviceClick, icon = { ArrowRightIcon() }, shouldShowVerifyLabel = true, - shouldShowE2EIInfo = item.e2eiCertificate != null + shouldShowE2EIInfo = item.mlsClientIdentity != null ) if (index < otherUserDevices.lastIndex) WireDivider() } diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt index 46b45d282d2..c6e9ab036a8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt @@ -70,9 +70,7 @@ import com.wire.kalium.logic.feature.conversation.UpdateConversationArchivedStat import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleResult import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationMutedStatusUseCase -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase import com.wire.kalium.logic.feature.user.GetUserInfoResult import com.wire.kalium.logic.feature.user.ObserveUserInfoUseCase @@ -107,7 +105,7 @@ class OtherUserProfileScreenViewModel @Inject constructor( private val fetchUsersClients: FetchUsersClientsFromRemoteUseCase, private val clearConversationContentUseCase: ClearConversationContentUseCase, private val updateConversationArchivedStatus: UpdateConversationArchivedStatusUseCase, - private val getUserE2eiCertificateStatus: GetUserE2eiCertificateStatusUseCase, + private val getUserE2eiCertificateStatus: IsOtherUserE2EIVerifiedUseCase, private val getUserE2eiCertificates: GetUserE2eiCertificatesUseCase, private val isOneToOneConversationCreated: IsOneToOneConversationCreatedUseCase, savedStateHandle: SavedStateHandle @@ -148,9 +146,7 @@ class OtherUserProfileScreenViewModel @Inject constructor( private fun getMLSVerificationStatus() { viewModelScope.launch { - val isMLSVerified = getUserE2eiCertificateStatus(userId).let { - it is GetUserE2eiCertificateStatusResult.Success && it.status == CertificateStatus.VALID - } + val isMLSVerified = getUserE2eiCertificateStatus(userId) state = state.copy(isMLSVerified = isMLSVerified) } } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt index 9d81a8a025b..a9f89c025e1 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt @@ -232,7 +232,11 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { // Default empty values coEvery { observeConversationMembersUseCase(any()) } returns flowOf() coEvery { membersHavingLegalHoldClientUseCase(any()) } returns Either.Right(emptyList()) - coEvery { getMembersE2EICertificateStatuses(any(), any()) } answers { secondArg>().associateWith { null } } + coEvery { + getMembersE2EICertificateStatuses(any(), any()) + } answers { + secondArg>().associateWith { false } + } } suspend fun withConversationParticipantsUpdate(members: List): ObserveParticipantsForConversationUseCaseArrangement { diff --git a/app/src/test/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModelTest.kt index 63e80a72688..080fece8d49 100644 --- a/app/src/test/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModelTest.kt @@ -27,9 +27,12 @@ import com.wire.android.ui.authentication.devices.remove.RemoveDeviceDialogState import com.wire.android.ui.authentication.devices.remove.RemoveDeviceError import com.wire.android.ui.navArgs import com.wire.android.ui.settings.devices.DeviceDetailsViewModelTest.Arrangement.Companion.CLIENT_ID +import com.wire.android.ui.settings.devices.DeviceDetailsViewModelTest.Arrangement.Companion.MLS_CLIENT_IDENTITY_WITH_VALID_E2EI import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.data.client.ClientType +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.id.QualifiedClientID import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.client.ClientFingerprintUseCase import com.wire.kalium.logic.feature.client.DeleteClientResult @@ -38,15 +41,18 @@ import com.wire.kalium.logic.feature.client.GetClientDetailsResult import com.wire.kalium.logic.feature.client.ObserveClientDetailsUseCase import com.wire.kalium.logic.feature.client.Result import com.wire.kalium.logic.feature.client.UpdateClientVerificationStatusUseCase -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2EICertificateUseCaseResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase +import com.wire.kalium.logic.feature.e2ei.Handle +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.feature.e2ei.MLSCredentialsType +import com.wire.kalium.logic.feature.e2ei.X509Identity +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase import com.wire.kalium.logic.feature.user.GetUserInfoResult import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase import com.wire.kalium.logic.feature.user.IsPasswordRequiredUseCase import com.wire.kalium.logic.feature.user.ObserveUserInfoUseCase -import com.wire.kalium.util.DateTimeUtil +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.right import io.mockk.Called import io.mockk.MockKAnnotations import io.mockk.coEvery @@ -58,11 +64,11 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant import okio.IOException import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith -import kotlin.time.Duration.Companion.days @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(CoroutineTestExtension::class) @@ -309,24 +315,16 @@ class DeviceDetailsViewModelTest { @Test fun `given a client with E2EI certificate, when fetching details, then returns device information`() { - val certificate = E2eiCertificate( - userHandle = "userHandle", - serialNumber = "serialNumber", - certificateDetail = "certificateDetail", - status = CertificateStatus.VALID, - thumbprint = "thumbprint", - endAt = DateTimeUtil.currentInstant().plus(1.days) - ) runTest { // given val (_, viewModel) = Arrangement() .withRequiredMockSetup() .withClientDetailsResult(GetClientDetailsResult.Success(TestClient.CLIENT, true)) - .withE2eiCertificate(GetE2EICertificateUseCaseResult.Success(certificate)) + .withE2eiCertificate(MLS_CLIENT_IDENTITY_WITH_VALID_E2EI.right()) .arrange() // then - assertEquals(certificate, viewModel.state.device.e2eiCertificate) + assertEquals(MLS_CLIENT_IDENTITY_WITH_VALID_E2EI, viewModel.state.device.mlsClientIdentity) } } @@ -354,7 +352,7 @@ class DeviceDetailsViewModelTest { lateinit var observeUserInfo: ObserveUserInfoUseCase @MockK - lateinit var getE2eiCertificate: GetE2eiCertificateUseCase + lateinit var getE2eiCertificate: GetMLSClientIdentityUseCase @MockK(relaxed = true) lateinit var onSuccess: () -> Unit @@ -383,7 +381,7 @@ class DeviceDetailsViewModelTest { MockKAnnotations.init(this, relaxUnitFun = true) withFingerprintSuccess() coEvery { observeUserInfo(any()) } returns flowOf(GetUserInfoResult.Success(TestUser.OTHER_USER, null)) - coEvery { getE2eiCertificate(any()) } returns GetE2EICertificateUseCaseResult.NotActivated + coEvery { getE2eiCertificate(any()) } returns MLS_CLIENT_IDENTITY_WITHOUT_E2EI.right() coEvery { isE2EIEnabledUseCase() } returns true } @@ -419,7 +417,7 @@ class DeviceDetailsViewModelTest { ) } - fun withE2eiCertificate(result: GetE2EICertificateUseCaseResult) = apply { + fun withE2eiCertificate(result: Either) = apply { coEvery { getE2eiCertificate(any()) } returns result } @@ -427,6 +425,28 @@ class DeviceDetailsViewModelTest { companion object { val CLIENT_ID = TestClient.CLIENT.id + val MLS_CLIENT_IDENTITY_WITH_VALID_E2EI = MLSClientIdentity( + clientId = QualifiedClientID(ClientId(""), UserId("", "")), + e2eiStatus = MLSClientE2EIStatus.VALID, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.X509, + x509Identity = X509Identity( + handle = Handle("", "", ""), + displayName = "", + domain = "", + certificate = "", + serialNumber = "e5:d5:e6:75:7e:04:86:07:14:3c:a0:ed:9a:8d:e4:fd", + notBefore = Instant.DISTANT_PAST, + notAfter = Instant.DISTANT_FUTURE + ) + ) + val MLS_CLIENT_IDENTITY_WITHOUT_E2EI = MLSClientIdentity( + clientId = QualifiedClientID(ClientId(""), UserId("", "")), + e2eiStatus = MLSClientE2EIStatus.NOT_ACTIVATED, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.BASIC, + x509Identity = null + ) } } } diff --git a/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt index 3dd80464f49..e550f0188dc 100644 --- a/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt @@ -44,9 +44,7 @@ import com.wire.kalium.logic.feature.conversation.UpdateConversationArchivedStat import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleResult import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationMutedStatusUseCase -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase import com.wire.kalium.logic.feature.user.GetSelfUserUseCase import com.wire.kalium.logic.feature.user.GetUserInfoResult @@ -108,7 +106,7 @@ internal class OtherUserProfileViewModelArrangement { lateinit var updateConversationArchivedStatus: UpdateConversationArchivedStatusUseCase @MockK - lateinit var getUserE2eiCertificateStatus: GetUserE2eiCertificateStatusUseCase + lateinit var getUserE2eiCertificateStatus: IsOtherUserE2EIVerifiedUseCase @MockK lateinit var getUserE2eiCertificates: GetUserE2eiCertificatesUseCase @@ -164,7 +162,7 @@ internal class OtherUserProfileViewModelArrangement { coEvery { getOneToOneConversation(USER_ID) } returns flowOf( GetOneToOneConversationUseCase.Result.Success(OtherUserProfileScreenViewModelTest.CONVERSATION) ) - coEvery { getUserE2eiCertificateStatus.invoke(any()) } returns GetUserE2eiCertificateStatusResult.Success(CertificateStatus.VALID) + coEvery { getUserE2eiCertificateStatus.invoke(any()) } returns true coEvery { getUserE2eiCertificates.invoke(any()) } returns mapOf() coEvery { isOneToOneConversationCreated.invoke(any()) } returns true } From 728e5bf9da47128431886d7dd75c53e676e74d0a Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 26 Jul 2024 15:02:54 +0200 Subject: [PATCH 3/7] chore: fix missing imports Signed-off-by: alexandreferris --- .../main/kotlin/com/wire/android/ui/WireActivity.kt | 11 ++++++++++- .../android/ui/e2eiEnrollment/E2EIEnrollmentScreen.kt | 9 ++++++++- .../ObserveParticipantsForConversationUseCase.kt | 3 +-- .../ui/settings/devices/DeviceDetailsScreen.kt | 7 ++++++- .../devices/e2ei/E2eiCertificateDetailsBottomSheet.kt | 9 +++++---- 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt index 96b34ea338e..6724af24f81 100644 --- a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt +++ b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt @@ -96,6 +96,7 @@ import com.wire.android.ui.legalhold.dialog.deactivated.LegalHoldDeactivatedView import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedDialog import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedState import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedViewModel +import com.wire.android.ui.settings.devices.e2ei.E2EICertificateDetails import com.wire.android.ui.theme.ThemeOption import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.userprofile.self.dialog.LogoutOptionsDialog @@ -416,7 +417,15 @@ class WireActivity : AppCompatActivity() { result = e2EIResult, updateCertificate = featureFlagNotificationViewModel::enrollE2EICertificate, snoozeDialog = featureFlagNotificationViewModel::snoozeE2EIdRequiredDialog, - openCertificateDetails = { navigate(NavigationCommand(E2eiCertificateDetailsScreenDestination(it))) }, + openCertificateDetails = { + navigate( + NavigationCommand( + E2eiCertificateDetailsScreenDestination( + E2EICertificateDetails.DuringLoginCertificateDetails(it) + ) + ) + ) + }, dismissSuccessDialog = featureFlagNotificationViewModel::dismissSuccessE2EIdDialog, isE2EILoading = isE2EILoading ) diff --git a/app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentScreen.kt b/app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentScreen.kt index ba7c74fbc12..09235816b2e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentScreen.kt @@ -56,6 +56,7 @@ import com.wire.android.ui.destinations.InitialSyncScreenDestination import com.wire.android.ui.home.E2EIEnrollmentErrorWithDismissDialog import com.wire.android.ui.home.E2EISuccessDialog import com.wire.android.ui.markdown.MarkdownConstants +import com.wire.android.ui.settings.devices.e2ei.E2EICertificateDetails import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography @@ -85,7 +86,13 @@ fun E2EIEnrollmentScreen( enrollE2EICertificate = viewModel::enrollE2EICertificate, handleE2EIEnrollmentResult = viewModel::handleE2EIEnrollmentResult, openCertificateDetails = { - navigator.navigate(NavigationCommand(E2eiCertificateDetailsScreenDestination(state.certificate))) + navigator.navigate( + NavigationCommand( + E2eiCertificateDetailsScreenDestination( + E2EICertificateDetails.DuringLoginCertificateDetails(state.certificate) + ) + ) + ) }, onBackButtonClicked = viewModel::onBackButtonClicked, onCancelEnrollmentClicked = { viewModel.onCancelEnrollmentClicked(NavigationSwitchAccountActions(navigator::navigate)) }, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt index 57bc5aeafad..35b9c0f536f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt @@ -77,10 +77,9 @@ class ObserveParticipantsForConversationUseCase @Inject constructor( getMembersE2EICertificateStatuses(conversationId, newlyEmittedVisibleUserIds) } ) - val legalHoldList = membersHavingLegalHoldClientUseCase(conversationId).getOrElse(emptyList()) fun List.toUIParticipants() = this.map { - uiParticipantMapper.toUIParticipant(it.user, mlsVerificationMap[it.userId], legalHoldList.contains(it.userId)) + uiParticipantMapper.toUIParticipant(it.user, mlsVerificationMap[it.userId].let { false }) } val selfUser = (allParticipants + allAdminsWithoutServices).firstOrNull { it.user is SelfUser } diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt index 9051100e785..d207bdfc2ac 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt @@ -76,6 +76,7 @@ import com.wire.android.ui.e2eiEnrollment.GetE2EICertificateUI import com.wire.android.ui.home.E2EISuccessDialog import com.wire.android.ui.home.E2EIUpdateErrorWithDismissDialog import com.wire.android.ui.home.conversationslist.common.FolderHeader +import com.wire.android.ui.settings.devices.e2ei.E2EICertificateDetails import com.wire.android.ui.settings.devices.model.DeviceDetailsState import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireColorScheme @@ -125,7 +126,11 @@ fun DeviceDetailsScreen( handleE2EIEnrollmentResult = viewModel::handleE2EIEnrollmentResult, onNavigateToE2eiCertificateDetailsScreen = { navigator.navigate( - NavigationCommand(E2eiCertificateDetailsScreenDestination(it)) + NavigationCommand( + E2eiCertificateDetailsScreenDestination( + E2EICertificateDetails.AfterLoginCertificateDetails(it) + ) + ) ) }, onEnrollE2EIErrorDismiss = viewModel::hideEnrollE2EICertificateError, diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt index 5ccd660cb55..bd840d4c82c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt @@ -28,7 +28,7 @@ import com.wire.android.ui.common.bottomsheet.MenuModalSheetContent import com.wire.android.ui.common.bottomsheet.MenuModalSheetHeader import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout import com.wire.android.ui.common.bottomsheet.WireModalSheetState -import com.wire.android.util.permission.rememberWriteStorageRequestFlow +import com.wire.android.util.permission.rememberWriteStoragePermissionFlow @Composable fun E2eiCertificateDetailsBottomSheet( @@ -37,9 +37,10 @@ fun E2eiCertificateDetailsBottomSheet( onDownload: () -> Unit, ) { val coroutineScope = rememberCoroutineScope() - val onSaveFileWriteStorageRequest = rememberWriteStorageRequestFlow( - onGranted = onDownload, - onDenied = { } + val onSaveFileWriteStorageRequest = rememberWriteStoragePermissionFlow( + onPermissionGranted = onDownload, + onPermissionDenied = { /** Nothing to do **/ }, + onPermissionPermanentlyDenied = { /** Nothing to do **/ } ) WireModalSheetLayout(sheetState = sheetState, coroutineScope = coroutineScope) { MenuModalSheetContent( From b0ad191d81d60750be504b391243f06fee878b2d Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 26 Jul 2024 17:24:58 +0200 Subject: [PATCH 4/7] chore: remove unused imports Signed-off-by: alexandreferris --- .../usecase/ObserveParticipantsForConversationUseCase.kt | 3 --- .../devices/e2ei/E2eiCertificateDetailsBottomSheet.kt | 4 ++-- .../usecase/ObserveParticipantsForConversationUseCaseTest.kt | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt index 35b9c0f536f..6c3050e6a09 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt @@ -32,8 +32,6 @@ import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCase -import com.wire.kalium.logic.feature.legalhold.MembersHavingLegalHoldClientUseCase -import com.wire.kalium.logic.functional.getOrElse import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.flowOn @@ -44,7 +42,6 @@ import javax.inject.Inject class ObserveParticipantsForConversationUseCase @Inject constructor( private val observeConversationMembers: ObserveConversationMembersUseCase, private val getMembersE2EICertificateStatuses: GetMembersE2EICertificateStatusesUseCase, - private val membersHavingLegalHoldClientUseCase: MembersHavingLegalHoldClientUseCase, private val uiParticipantMapper: UIParticipantMapper, private val dispatchers: DispatcherProvider ) { diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt index bd840d4c82c..5bfe364b670 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsBottomSheet.kt @@ -39,8 +39,8 @@ fun E2eiCertificateDetailsBottomSheet( val coroutineScope = rememberCoroutineScope() val onSaveFileWriteStorageRequest = rememberWriteStoragePermissionFlow( onPermissionGranted = onDownload, - onPermissionDenied = { /** Nothing to do **/ }, - onPermissionPermanentlyDenied = { /** Nothing to do **/ } + onPermissionDenied = { }, + onPermissionPermanentlyDenied = { } ) WireModalSheetLayout(sheetState = sheetState, coroutineScope = coroutineScope) { MenuModalSheetContent( diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt index a9f89c025e1..8c90cac2b60 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt @@ -220,7 +220,6 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { ObserveParticipantsForConversationUseCase( observeConversationMembersUseCase, getMembersE2EICertificateStatuses, - membersHavingLegalHoldClientUseCase, uIParticipantMapper, dispatchers = TestDispatcherProvider() ) From 4de12ca753ace3c50a8e269094d2a762547c6854 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 26 Jul 2024 18:36:21 +0200 Subject: [PATCH 5/7] chore: remove unused extension function Signed-off-by: alexandreferris --- .../usecase/ObserveParticipantsForConversationUseCase.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt index 6c3050e6a09..9778db64384 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt @@ -75,9 +75,6 @@ class ObserveParticipantsForConversationUseCase @Inject constructor( } ) - fun List.toUIParticipants() = this.map { - uiParticipantMapper.toUIParticipant(it.user, mlsVerificationMap[it.userId].let { false }) - } val selfUser = (allParticipants + allAdminsWithoutServices).firstOrNull { it.user is SelfUser } ConversationParticipantsData( From 9b12fe8bf0bdfa6b3fde5fb7ea94b209e3660411 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 26 Jul 2024 18:36:29 +0200 Subject: [PATCH 6/7] tests: fix tests with missing values Signed-off-by: alexandreferris --- .../android/mapper/UIParticipantMapperTest.kt | 3 ++- ...erveParticipantsForConversationUseCaseTest.kt | 16 +++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt index c33e6700601..a4d9fe75cc8 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt @@ -161,7 +161,8 @@ fun testOtherUser(i: Int): OtherUser = OtherUser( deleted = false, defederated = false, isProteusVerified = false, - supportedProtocols = setOf(SupportedProtocol.PROTEUS) + supportedProtocols = setOf(SupportedProtocol.PROTEUS), + isUnderLegalHold = false ) fun testUIParticipant(i: Int): UIParticipant = UIParticipant( diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt index 8c90cac2b60..cc287eb8401 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt @@ -31,8 +31,6 @@ import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCase -import com.wire.kalium.logic.feature.legalhold.MembersHavingLegalHoldClientUseCase -import com.wire.kalium.logic.functional.Either import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.coVerify @@ -91,12 +89,12 @@ class ObserveParticipantsForConversationUseCaseTest { @Test fun givenGroupMembersUnderLegalHold_whenSolvingTheParticipantsList_thenPassCorrectLegalHoldValues() = runTest { // Given - val memberUnderLegalHold = MemberDetails(testOtherUser(0).copy(userType = UserType.INTERNAL), Member.Role.Member) - val memberNotUnderLegalHold = MemberDetails(testOtherUser(1).copy(userType = UserType.INTERNAL), Member.Role.Member) + val memberUnderLegalHold = MemberDetails(testOtherUser(0).copy(userType = UserType.INTERNAL, isUnderLegalHold = true), Member.Role.Member) + val memberNotUnderLegalHold = MemberDetails(testOtherUser(1).copy(userType = UserType.INTERNAL, isUnderLegalHold = false), Member.Role.Member) val (_, useCase) = ObserveParticipantsForConversationUseCaseArrangement() .withConversationParticipantsUpdate(listOf(memberUnderLegalHold, memberNotUnderLegalHold)) - .withMembersHavingLegalHoldClient(listOf(memberUnderLegalHold.user.id)) .arrange() + // When - Then useCase(ConversationId("", "")).test { val data = awaitItem() @@ -209,9 +207,6 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { @MockK lateinit var getMembersE2EICertificateStatuses: GetMembersE2EICertificateStatusesUseCase - @MockK - lateinit var membersHavingLegalHoldClientUseCase: MembersHavingLegalHoldClientUseCase - @MockK private lateinit var wireSessionImageLoader: WireSessionImageLoader private val uIParticipantMapper by lazy { UIParticipantMapper(UserTypeMapper(), wireSessionImageLoader) } @@ -230,7 +225,6 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { MockKAnnotations.init(this, relaxUnitFun = true) // Default empty values coEvery { observeConversationMembersUseCase(any()) } returns flowOf() - coEvery { membersHavingLegalHoldClientUseCase(any()) } returns Either.Right(emptyList()) coEvery { getMembersE2EICertificateStatuses(any(), any()) } answers { @@ -244,9 +238,5 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { return this } - suspend fun withMembersHavingLegalHoldClient(members: List) = apply { - coEvery { membersHavingLegalHoldClientUseCase(any()) } returns Either.Right(members) - } - fun arrange() = this to useCase } From 18e389554a87f74ad917734b1a96505c7986604a Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 26 Jul 2024 18:47:06 +0200 Subject: [PATCH 7/7] chore: fix detekt Signed-off-by: alexandreferris --- .../ObserveParticipantsForConversationUseCase.kt | 1 - .../ObserveParticipantsForConversationUseCaseTest.kt | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt index 9778db64384..7c3d0d58f00 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCase.kt @@ -24,7 +24,6 @@ import com.wire.android.ui.home.conversations.name import com.wire.android.ui.home.conversations.userId import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.kalium.logic.data.conversation.Conversation.Member -import com.wire.kalium.logic.data.conversation.MemberDetails import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.OtherUser import com.wire.kalium.logic.data.user.SelfUser diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt index cc287eb8401..6eb44e1f24e 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt @@ -89,8 +89,14 @@ class ObserveParticipantsForConversationUseCaseTest { @Test fun givenGroupMembersUnderLegalHold_whenSolvingTheParticipantsList_thenPassCorrectLegalHoldValues() = runTest { // Given - val memberUnderLegalHold = MemberDetails(testOtherUser(0).copy(userType = UserType.INTERNAL, isUnderLegalHold = true), Member.Role.Member) - val memberNotUnderLegalHold = MemberDetails(testOtherUser(1).copy(userType = UserType.INTERNAL, isUnderLegalHold = false), Member.Role.Member) + val memberUnderLegalHold = MemberDetails( + user = testOtherUser(0).copy(userType = UserType.INTERNAL, isUnderLegalHold = true), + role = Member.Role.Member + ) + val memberNotUnderLegalHold = MemberDetails( + user = testOtherUser(1).copy(userType = UserType.INTERNAL, isUnderLegalHold = false), + role = Member.Role.Member + ) val (_, useCase) = ObserveParticipantsForConversationUseCaseArrangement() .withConversationParticipantsUpdate(listOf(memberUnderLegalHold, memberNotUnderLegalHold)) .arrange()