Verified user badge.
Add disable action to verify user.
This commit is contained in:
parent
639089718d
commit
200ae60a8b
10 changed files with 107 additions and 20 deletions
|
|
@ -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?,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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>()
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue