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 8efbd67eea
commit 5378c4efad
10 changed files with 107 additions and 20 deletions

View file

@ -18,9 +18,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
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.AvatarData
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.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.toImmutableList
@Composable
fun UserProfileHeaderSection(
avatarUrl: String?,
userId: UserId,
userName: String?,
isUserVerified: AsyncData<Boolean>,
openAvatarPreview: (url: String) -> Unit,
modifier: Modifier = Modifier
) {
@ -67,6 +75,17 @@ fun UserProfileHeaderSection(
color = MaterialTheme.colorScheme.secondary,
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))
}
}
@ -78,6 +97,7 @@ internal fun UserProfileHeaderSectionPreview() = ElementPreview {
avatarUrl = null,
userId = UserId("@alice:example.com"),
userName = "Alice",
isUserVerified = AsyncData.Success(true),
openAvatarPreview = {},
)
}

View file

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

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
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.UserProfileState
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.AsyncActionViewDefaults
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.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.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@ -63,11 +68,11 @@ fun UserProfileView(
avatarUrl = state.avatarUrl,
userId = state.userId,
userName = state.userName,
isUserVerified = state.isVerified,
openAvatarPreview = { avatarUrl ->
openAvatarPreview(state.userName ?: state.userId.value, avatarUrl)
},
)
UserProfileMainActionsSection(
isCurrentUser = state.isCurrentUser,
canCall = state.canCall,
@ -75,10 +80,9 @@ fun UserProfileView(
onStartDM = { state.eventSink(UserProfileEvents.StartDM) },
onCall = { state.dmRoomId?.let { onStartCall(it) } }
)
Spacer(modifier = Modifier.height(26.dp))
if (!state.isCurrentUser) {
VerifyUserSection(state)
BlockUserSection(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
@Composable
internal fun UserProfileViewPreview(

View file

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