Verified user badge.

Add disable action to verify user.
This commit is contained in:
Benoit Marty 2024-10-16 22:28:16 +02:00 committed by Benoit Marty
parent 639089718d
commit 200ae60a8b
10 changed files with 107 additions and 20 deletions

View file

@ -16,6 +16,7 @@ data class UserProfileState(
val userId: UserId, val userId: UserId,
val userName: String?, val userName: String?,
val avatarUrl: String?, val avatarUrl: String?,
val isVerified: AsyncData<Boolean>,
val isBlocked: AsyncData<Boolean>, val isBlocked: AsyncData<Boolean>,
val startDmActionState: AsyncAction<RoomId>, val startDmActionState: AsyncAction<RoomId>,
val displayConfirmationDialog: ConfirmationDialog?, val displayConfirmationDialog: ConfirmationDialog?,

View file

@ -27,6 +27,7 @@ import io.element.android.features.userprofile.api.UserProfileState.Confirmation
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@ -75,6 +76,7 @@ class UserProfilePresenter @AssistedInject constructor(
var userProfile by remember { mutableStateOf<MatrixUser?>(null) } var userProfile by remember { mutableStateOf<MatrixUser?>(null) }
val startDmActionState: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) } val startDmActionState: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
val isBlocked: MutableState<AsyncData<Boolean>> = remember { mutableStateOf(AsyncData.Uninitialized) } val isBlocked: MutableState<AsyncData<Boolean>> = remember { mutableStateOf(AsyncData.Uninitialized) }
val isVerified: MutableState<AsyncData<Boolean>> = remember { mutableStateOf(AsyncData.Uninitialized) }
val dmRoomId by getDmRoomId() val dmRoomId by getDmRoomId()
val canCall by getCanCall(dmRoomId) val canCall by getCanCall(dmRoomId)
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@ -87,6 +89,11 @@ class UserProfilePresenter @AssistedInject constructor(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
userProfile = client.getProfile(userId).getOrNull() userProfile = client.getProfile(userId).getOrNull()
} }
LaunchedEffect(Unit) {
suspend {
client.encryptionService().isUserVerified(userId).getOrThrow()
}.runCatchingUpdatingState(isVerified)
}
fun handleEvents(event: UserProfileEvents) { fun handleEvents(event: UserProfileEvents) {
when (event) { when (event) {
@ -126,6 +133,7 @@ class UserProfilePresenter @AssistedInject constructor(
userName = userProfile?.displayName, userName = userProfile?.displayName,
avatarUrl = userProfile?.avatarUrl, avatarUrl = userProfile?.avatarUrl,
isBlocked = isBlocked.value, isBlocked = isBlocked.value,
isVerified = isVerified.value,
startDmActionState = startDmActionState.value, startDmActionState = startDmActionState.value,
displayConfirmationDialog = confirmationDialog, displayConfirmationDialog = confirmationDialog,
isCurrentUser = isCurrentUser, isCurrentUser = isCurrentUser,

View file

@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -43,7 +44,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - returns the user profile data`() = runTest { fun `present - returns the user profile data`() = runTest {
val matrixUser = aMatrixUser(A_USER_ID.value, "Alice", "anAvatarUrl") val matrixUser = aMatrixUser(A_USER_ID.value, "Alice", "anAvatarUrl")
val client = FakeMatrixClient().apply { val client = createFakeMatrixClient().apply {
givenGetProfileResult(A_USER_ID, Result.success(matrixUser)) givenGetProfileResult(A_USER_ID, Result.success(matrixUser))
} }
val presenter = createUserProfilePresenter( val presenter = createUserProfilePresenter(
@ -55,6 +56,7 @@ class UserProfilePresenterTest {
assertThat(initialState.userName).isEqualTo(matrixUser.displayName) assertThat(initialState.userName).isEqualTo(matrixUser.displayName)
assertThat(initialState.avatarUrl).isEqualTo(matrixUser.avatarUrl) assertThat(initialState.avatarUrl).isEqualTo(matrixUser.avatarUrl)
assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(false)) assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(false))
assertThat(initialState.isVerified.dataOrNull()).isFalse()
assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID) assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID)
assertThat(initialState.canCall).isFalse() assertThat(initialState.canCall).isFalse()
} }
@ -108,7 +110,7 @@ class UserProfilePresenterTest {
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
canUserJoinCallResult = { canUserJoinCallResult }, canUserJoinCallResult = { canUserJoinCallResult },
) )
val client = FakeMatrixClient().apply { val client = createFakeMatrixClient().apply {
if (canFindRoom) { if (canFindRoom) {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
} }
@ -126,7 +128,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - returns empty data in case of failure`() = runTest { fun `present - returns empty data in case of failure`() = runTest {
val client = FakeMatrixClient().apply { val client = createFakeMatrixClient().apply {
givenGetProfileResult(A_USER_ID, Result.failure(AN_EXCEPTION)) givenGetProfileResult(A_USER_ID, Result.failure(AN_EXCEPTION))
} }
val presenter = createUserProfilePresenter( val presenter = createUserProfilePresenter(
@ -153,14 +155,12 @@ class UserProfilePresenterTest {
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull() assertThat(awaitItem().displayConfirmationDialog).isNull()
ensureAllEventsConsumed()
} }
} }
@Test @Test
fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest { fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest {
val client = FakeMatrixClient() val client = createFakeMatrixClient()
val presenter = createUserProfilePresenter( val presenter = createUserProfilePresenter(
client = client, client = client,
userId = A_USER_ID userId = A_USER_ID
@ -181,7 +181,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - BlockUser with error`() = runTest { fun `present - BlockUser with error`() = runTest {
val matrixClient = FakeMatrixClient() val matrixClient = createFakeMatrixClient()
matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE))
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
@ -198,7 +198,7 @@ class UserProfilePresenterTest {
@Test @Test
fun `present - UnblockUser with error`() = runTest { fun `present - UnblockUser with error`() = runTest {
val matrixClient = FakeMatrixClient() val matrixClient = createFakeMatrixClient()
matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE)) matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE))
val presenter = createUserProfilePresenter(client = matrixClient) val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test { presenter.test {
@ -225,8 +225,6 @@ class UserProfilePresenterTest {
dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog) dialogState.eventSink(UserProfileEvents.ClearConfirmationDialog)
assertThat(awaitItem().displayConfirmationDialog).isNull() assertThat(awaitItem().displayConfirmationDialog).isNull()
ensureAllEventsConsumed()
} }
} }
@ -262,13 +260,34 @@ class UserProfilePresenterTest {
} }
} }
@Test
fun `present - when user is verified, the value in the state is true`() = runTest {
val client = createFakeMatrixClient(isUserVerified = true)
val presenter = createUserProfilePresenter(
client = client,
)
presenter.test {
assertThat(awaitItem().isVerified.isUninitialized()).isTrue()
assertThat(awaitItem().isVerified.isLoading()).isTrue()
assertThat(awaitItem().isVerified.dataOrNull()).isTrue()
}
}
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T { private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
skipItems(1) skipItems(2)
return awaitItem() return awaitItem()
} }
private fun createFakeMatrixClient(
isUserVerified: Boolean = false,
) = FakeMatrixClient(
encryptionService = FakeEncryptionService(
isUserVerifiedResult = { Result.success(isUserVerified) }
),
)
private fun createUserProfilePresenter( private fun createUserProfilePresenter(
client: MatrixClient = FakeMatrixClient(), client: MatrixClient = createFakeMatrixClient(),
userId: UserId = UserId("@alice:server.org"), userId: UserId = UserId("@alice:server.org"),
startDMAction: StartDMAction = FakeStartDMAction() startDMAction: StartDMAction = FakeStartDMAction()
): UserProfilePresenter { ): UserProfilePresenter {

View file

@ -18,9 +18,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.atomic.atoms.MatrixBadgeAtom
import io.element.android.libraries.designsystem.atomic.molecules.MatrixBadgeRowMolecule
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -30,12 +35,15 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.toImmutableList
@Composable @Composable
fun UserProfileHeaderSection( fun UserProfileHeaderSection(
avatarUrl: String?, avatarUrl: String?,
userId: UserId, userId: UserId,
userName: String?, userName: String?,
isUserVerified: AsyncData<Boolean>,
openAvatarPreview: (url: String) -> Unit, openAvatarPreview: (url: String) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@ -67,6 +75,17 @@ fun UserProfileHeaderSection(
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
if (isUserVerified.dataOrNull() == true) {
MatrixBadgeRowMolecule(
data = listOf(
MatrixBadgeAtom.MatrixBadgeData(
text = stringResource(CommonStrings.common_verified),
icon = CompoundIcons.Verified(),
type = MatrixBadgeAtom.Type.Positive,
)
).toImmutableList(),
)
}
Spacer(Modifier.height(40.dp)) Spacer(Modifier.height(40.dp))
} }
} }
@ -78,6 +97,7 @@ internal fun UserProfileHeaderSectionPreview() = ElementPreview {
avatarUrl = null, avatarUrl = null,
userId = UserId("@alice:example.com"), userId = UserId("@alice:example.com"),
userName = "Alice", userName = "Alice",
isUserVerified = AsyncData.Success(true),
openAvatarPreview = {}, openAvatarPreview = {},
) )
} }

View file

@ -20,13 +20,12 @@ open class UserProfileStateProvider : PreviewParameterProvider<UserProfileState>
get() = sequenceOf( get() = sequenceOf(
aUserProfileState(), aUserProfileState(),
aUserProfileState(userName = null), aUserProfileState(userName = null),
aUserProfileState(isBlocked = AsyncData.Success(true)), aUserProfileState(isBlocked = AsyncData.Success(true), isVerified = AsyncData.Success(true)),
aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block), aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block),
aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock), aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock),
aUserProfileState(isBlocked = AsyncData.Loading(true)), aUserProfileState(isBlocked = AsyncData.Loading(true), isVerified = AsyncData.Loading()),
aUserProfileState(startDmActionState = AsyncAction.Loading), aUserProfileState(startDmActionState = AsyncAction.Loading),
aUserProfileState(canCall = true), aUserProfileState(canCall = true),
aUserProfileState(dmRoomId = null),
// Add other states here // Add other states here
) )
} }
@ -36,6 +35,7 @@ fun aUserProfileState(
userName: String? = "Daniel", userName: String? = "Daniel",
avatarUrl: String? = null, avatarUrl: String? = null,
isBlocked: AsyncData<Boolean> = AsyncData.Success(false), isBlocked: AsyncData<Boolean> = AsyncData.Success(false),
isVerified: AsyncData<Boolean> = AsyncData.Success(false),
startDmActionState: AsyncAction<RoomId> = AsyncAction.Uninitialized, startDmActionState: AsyncAction<RoomId> = AsyncAction.Uninitialized,
displayConfirmationDialog: UserProfileState.ConfirmationDialog? = null, displayConfirmationDialog: UserProfileState.ConfirmationDialog? = null,
isCurrentUser: Boolean = false, isCurrentUser: Boolean = false,
@ -47,6 +47,7 @@ fun aUserProfileState(
userName = userName, userName = userName,
avatarUrl = avatarUrl, avatarUrl = avatarUrl,
isBlocked = isBlocked, isBlocked = isBlocked,
isVerified = isVerified,
startDmActionState = startDmActionState, startDmActionState = startDmActionState,
displayConfirmationDialog = displayConfirmationDialog, displayConfirmationDialog = displayConfirmationDialog,
isCurrentUser = isCurrentUser, isCurrentUser = isCurrentUser,

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvents
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs
@ -28,9 +29,13 @@ import io.element.android.features.userprofile.shared.blockuser.BlockUserSection
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -63,11 +68,11 @@ fun UserProfileView(
avatarUrl = state.avatarUrl, avatarUrl = state.avatarUrl,
userId = state.userId, userId = state.userId,
userName = state.userName, userName = state.userName,
isUserVerified = state.isVerified,
openAvatarPreview = { avatarUrl -> openAvatarPreview = { avatarUrl ->
openAvatarPreview(state.userName ?: state.userId.value, avatarUrl) openAvatarPreview(state.userName ?: state.userId.value, avatarUrl)
}, },
) )
UserProfileMainActionsSection( UserProfileMainActionsSection(
isCurrentUser = state.isCurrentUser, isCurrentUser = state.isCurrentUser,
canCall = state.canCall, canCall = state.canCall,
@ -75,10 +80,9 @@ fun UserProfileView(
onStartDM = { state.eventSink(UserProfileEvents.StartDM) }, onStartDM = { state.eventSink(UserProfileEvents.StartDM) },
onCall = { state.dmRoomId?.let { onStartCall(it) } } onCall = { state.dmRoomId?.let { onStartCall(it) } }
) )
Spacer(modifier = Modifier.height(26.dp)) Spacer(modifier = Modifier.height(26.dp))
if (!state.isCurrentUser) { if (!state.isCurrentUser) {
VerifyUserSection(state)
BlockUserSection(state) BlockUserSection(state)
BlockUserDialogs(state) BlockUserDialogs(state)
} }
@ -98,6 +102,19 @@ fun UserProfileView(
} }
} }
@Composable
private fun VerifyUserSection(state: UserProfileState) {
if (state.isVerified.dataOrNull() == false) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_member_details_verify_button_title, state.userName ?: state.userId)) },
supportingContent = { Text(stringResource(R.string.screen_room_member_details_verify_button_subtitle)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
enabled = false,
onClick = { },
)
}
}
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun UserProfileViewPreview( internal fun UserProfileViewPreview(

View file

@ -40,6 +40,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TestRule import org.junit.rules.TestRule
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class UserProfileViewTest { class UserProfileViewTest {
@ -123,6 +124,7 @@ class UserProfileViewTest {
} }
} }
@Config(qualifiers = "h1024dp")
@Test @Test
fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runTest { fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvents>()
@ -161,6 +163,7 @@ class UserProfileViewTest {
eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog)
} }
@Config(qualifiers = "h1024dp")
@Test @Test
fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runTest { fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runTest {
val eventsRecorder = EventsRecorder<UserProfileEvents>() val eventsRecorder = EventsRecorder<UserProfileEvents>()

View file

@ -60,6 +60,8 @@ interface EncryptionService {
*/ */
suspend fun startIdentityReset(): Result<IdentityResetHandle?> suspend fun startIdentityReset(): Result<IdentityResetHandle?>
suspend fun isUserVerified(userId: UserId): Result<Boolean>
/** /**
* Remember this identity, ensuring it does not result in a pin violation. * Remember this identity, ensuring it does not result in a pin violation.
*/ */

View file

@ -38,6 +38,7 @@ import org.matrix.rustcomponents.sdk.BackupSteadyStateListener
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.EnableRecoveryProgressListener import org.matrix.rustcomponents.sdk.EnableRecoveryProgressListener
import org.matrix.rustcomponents.sdk.Encryption import org.matrix.rustcomponents.sdk.Encryption
import org.matrix.rustcomponents.sdk.UserIdentity
import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
@ -204,8 +205,18 @@ internal class RustEncryptionService(
} }
} }
override suspend fun isUserVerified(userId: UserId): Result<Boolean> = runCatching {
getUserIdentity(userId).isVerified()
}
override suspend fun pinUserIdentity(userId: UserId): Result<Unit> = runCatching { override suspend fun pinUserIdentity(userId: UserId): Result<Unit> = runCatching {
val userIdentity = service.userIdentity(userId.value) ?: error("User identity not found") getUserIdentity(userId).pin()
userIdentity.pin() }
private suspend fun getUserIdentity(userId: UserId): UserIdentity {
return service.userIdentity(
userId = userId.value,
// requestFromHomeserverIfNeeded = true,
) ?: error("User identity not found")
} }
} }

View file

@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.flowOf
class FakeEncryptionService( class FakeEncryptionService(
var startIdentityResetLambda: () -> Result<IdentityResetHandle?> = { lambdaError() }, var startIdentityResetLambda: () -> Result<IdentityResetHandle?> = { lambdaError() },
private val pinUserIdentityResult: (UserId) -> Result<Unit> = { lambdaError() }, private val pinUserIdentityResult: (UserId) -> Result<Unit> = { lambdaError() },
private val isUserVerifiedResult: (UserId) -> Result<Boolean> = { lambdaError() },
) : EncryptionService { ) : EncryptionService {
private var disableRecoveryFailure: Exception? = null private var disableRecoveryFailure: Exception? = null
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN) override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN)
@ -123,6 +124,10 @@ class FakeEncryptionService(
return pinUserIdentityResult(userId) return pinUserIdentityResult(userId)
} }
override suspend fun isUserVerified(userId: UserId): Result<Boolean> = simulateLongTask {
isUserVerifiedResult(userId)
}
companion object { companion object {
const val FAKE_RECOVERY_KEY = "fake" const val FAKE_RECOVERY_KEY = "fake"
} }