diff --git a/changelog.d/2818.misc b/changelog.d/2818.misc new file mode 100644 index 00000000000..6146c2ca826 --- /dev/null +++ b/changelog.d/2818.misc @@ -0,0 +1 @@ +UX cleanup: user profile. Move send DM to a call to action button, add 'Call' CTA too. diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 460d3038acc..798a9032745 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -188,6 +188,10 @@ class RoomDetailsFlowNode @AssistedInject constructor( override fun onStartDM(roomId: RoomId) { plugins().forEach { it.onOpenRoom(roomId) } } + + override fun onStartCall(roomId: RoomId) { + ElementCallActivity.start(context, CallType.RoomCall(roomId = roomId, sessionId = room.sessionId)) + } } val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback) createNode(buildContext, plugins) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt index 14ab79df4ca..caccbc97beb 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt @@ -76,6 +76,10 @@ class RoomMemberDetailsNode @AssistedInject constructor( callback.onStartDM(roomId) } + fun onStartCall(roomId: RoomId) { + callback.onStartCall(roomId) + } + val state = presenter.present() LaunchedEffect(state.startDmActionState) { @@ -89,7 +93,8 @@ class RoomMemberDetailsNode @AssistedInject constructor( modifier = modifier, goBack = this::navigateUp, onShareUser = ::onShareUser, - onDMStarted = ::onStartDM, + onDmStarted = ::onStartDM, + onStartCall = ::onStartCall, openAvatarPreview = callback::openAvatarPreview, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 4ac9da2e193..78dcea4c6d8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -71,6 +71,9 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( var userProfile by remember { mutableStateOf(null) } val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val isBlocked: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } + val isCurrentUser = remember { client.isMe(roomMemberId) } + val dmRoomId by userProfilePresenterHelper.getDmRoomId() + val canCall by userProfilePresenterHelper.getCanCall(dmRoomId) LaunchedEffect(Unit) { client.ignoredUsersFlow .map { ignoredUsers -> roomMemberId in ignoredUsers } @@ -158,7 +161,9 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( isBlocked = isBlocked.value, startDmActionState = startDmActionState.value, displayConfirmationDialog = confirmationDialog, - isCurrentUser = client.isMe(roomMemberId), + isCurrentUser = isCurrentUser, + dmRoomId = dmRoomId, + canCall = canCall, eventSink = ::handleEvents ) } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt index c615ba3da97..89b7335fefd 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt @@ -70,6 +70,8 @@ class RoomMemberDetailsPresenterTests { assertThat(initialState.userName).isEqualTo(roomMember.displayName) assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl) assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(roomMember.isIgnored)) + assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID) + assertThat(initialState.canCall).isFalse() skipItems(1) val loadedState = awaitItem() assertThat(loadedState.userName).isEqualTo("A custom name") diff --git a/features/userprofile/impl/build.gradle.kts b/features/userprofile/impl/build.gradle.kts index 11a2cd867ba..e41524abb21 100644 --- a/features/userprofile/impl/build.gradle.kts +++ b/features/userprofile/impl/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) implementation(projects.libraries.mediaviewer.api) + implementation(projects.features.call) api(projects.features.userprofile.api) api(projects.features.userprofile.shared) implementation(libs.coil.compose) diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index 43402fa0156..402e07dbbad 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -16,6 +16,7 @@ package io.element.android.features.userprofile.impl +import android.content.Context import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -28,6 +29,8 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.call.CallType +import io.element.android.features.call.ui.ElementCallActivity import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.userprofile.impl.root.UserProfileNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper @@ -37,9 +40,11 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import kotlinx.parcelize.Parcelize @@ -48,6 +53,8 @@ import kotlinx.parcelize.Parcelize class UserProfileFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + @ApplicationContext private val context: Context, + private val sessionIdHolder: CurrentSessionIdHolder, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -75,6 +82,10 @@ class UserProfileFlowNode @AssistedInject constructor( override fun onStartDM(roomId: RoomId) { plugins().forEach { it.onOpenRoom(roomId) } } + + override fun onStartCall(roomId: RoomId) { + ElementCallActivity.start(context, CallType.RoomCall(sessionId = sessionIdHolder.current, roomId = roomId)) + } } val params = UserProfileNode.UserProfileInputs(userId = inputs().userId) createNode(buildContext, listOf(callback, params)) diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt index 9e1dc5e873e..4d4ea993c49 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfileNode.kt @@ -89,7 +89,8 @@ class UserProfileNode @AssistedInject constructor( modifier = modifier, goBack = this::navigateUp, onShareUser = ::onShareUser, - onDMStarted = ::onStartDM, + onDmStarted = ::onStartDM, + onStartCall = callback::onStartCall, openAvatarPreview = callback::openAvatarPreview, ) } diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt index fdd2038c7f2..753891156e3 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/root/UserProfilePresenter.kt @@ -66,6 +66,8 @@ class UserProfilePresenter @AssistedInject constructor( var userProfile by remember { mutableStateOf(null) } val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val isBlocked: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } + val dmRoomId by userProfilePresenterHelper.getDmRoomId() + val canCall by userProfilePresenterHelper.getCanCall(dmRoomId) LaunchedEffect(Unit) { client.ignoredUsersFlow .map { ignoredUsers -> userId in ignoredUsers } @@ -118,6 +120,8 @@ class UserProfilePresenter @AssistedInject constructor( startDmActionState = startDmActionState.value, displayConfirmationDialog = confirmationDialog, isCurrentUser = client.isMe(userId), + dmRoomId = dmRoomId, + canCall = canCall, eventSink = ::handleEvents ) } diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTests.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTests.kt index f0d5955ce94..20b63ef7026 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTests.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTests.kt @@ -64,6 +64,8 @@ class UserProfilePresenterTests { assertThat(initialState.userName).isEqualTo(matrixUser.displayName) assertThat(initialState.avatarUrl).isEqualTo(matrixUser.avatarUrl) assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(false)) + assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID) + assertThat(initialState.canCall).isFalse() } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt index 23241bbcc2b..9db6aee2b9f 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt @@ -49,7 +49,12 @@ fun UserProfileHeaderSection( openAvatarPreview: (url: String) -> Unit, modifier: Modifier = Modifier ) { - Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { Box(modifier = Modifier.size(70.dp)) { Avatar( avatarData = AvatarData(userId.value, userName, avatarUrl, AvatarSize.UserHeader), @@ -65,6 +70,7 @@ fun UserProfileHeaderSection( modifier = Modifier.clipToBounds(), text = userName, style = ElementTheme.typography.fontHeadingLgBold, + textAlign = TextAlign.Center, ) Spacer(modifier = Modifier.height(6.dp)) } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileMainActionsSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileMainActionsSection.kt index ec27a18dcd7..d194a5662a5 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileMainActionsSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileMainActionsSection.kt @@ -29,11 +29,32 @@ import io.element.android.libraries.designsystem.components.button.MainActionBut import io.element.android.libraries.ui.strings.CommonStrings @Composable -fun UserProfileMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) { +fun UserProfileMainActionsSection( + isCurrentUser: Boolean, + canCall: Boolean, + onShareUser: () -> Unit, + onStartDM: () -> Unit, + onCall: () -> Unit, + modifier: Modifier = Modifier +) { Row( modifier.fillMaxWidth().padding(horizontal = 16.dp), horizontalArrangement = Arrangement.SpaceEvenly, ) { + if (!isCurrentUser) { + MainActionButton( + title = stringResource(CommonStrings.action_message), + imageVector = CompoundIcons.Chat(), + onClick = onStartDM, + ) + } + if (canCall) { + MainActionButton( + title = stringResource(CommonStrings.action_call), + imageVector = CompoundIcons.VideoCall(), + onClick = onCall, + ) + } MainActionButton( title = stringResource(CommonStrings.action_share), imageVector = CompoundIcons.ShareAndroid(), diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt index 60afcae25bf..7a669772f7f 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt @@ -32,6 +32,7 @@ class UserProfileNodeHelper( interface Callback : NodeInputs { fun openAvatarPreview(username: String, avatarUrl: String) fun onStartDM(roomId: RoomId) + fun onStartCall(roomId: RoomId) } fun onShareUser( diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt index df40a354329..af2311aab75 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfilePresenterHelper.kt @@ -16,9 +16,14 @@ package io.element.android.features.userprofile.shared +import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.produceState import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.core.bool.orFalse 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.UserId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -27,6 +32,24 @@ class UserProfilePresenterHelper( private val userId: UserId, private val client: MatrixClient, ) { + @Composable + fun getDmRoomId(): State { + return produceState(initialValue = null) { + value = client.findDM(userId) + } + } + + @Composable + fun getCanCall(roomId: RoomId?): State { + return produceState(initialValue = false, roomId) { + value = if (client.isMe(userId)) { + false + } else { + roomId?.let { client.getRoom(it)?.canUserJoinCall(client.sessionId)?.getOrNull() == true }.orFalse() + } + } + } + fun blockUser( scope: CoroutineScope, isBlockedState: MutableState>, diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt index e36edd36c0c..bdf4578172d 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileState.kt @@ -29,6 +29,8 @@ data class UserProfileState( val startDmActionState: AsyncAction, val displayConfirmationDialog: ConfirmationDialog?, val isCurrentUser: Boolean, + val dmRoomId: RoomId?, + val canCall: Boolean, val eventSink: (UserProfileEvents) -> Unit ) { enum class ConfirmationDialog { diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt index 8892c608514..14b5d298788 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt @@ -32,6 +32,8 @@ open class UserProfileStateProvider : PreviewParameterProvider aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock), aUserProfileState(isBlocked = AsyncData.Loading(true)), aUserProfileState(startDmActionState = AsyncAction.Loading), + aUserProfileState(canCall = true), + aUserProfileState(dmRoomId = null), // Add other states here ) } @@ -44,6 +46,8 @@ fun aUserProfileState( startDmActionState: AsyncAction = AsyncAction.Uninitialized, displayConfirmationDialog: UserProfileState.ConfirmationDialog? = null, isCurrentUser: Boolean = false, + dmRoomId: RoomId? = null, + canCall: Boolean = false, eventSink: (UserProfileEvents) -> Unit = {}, ) = UserProfileState( userId = userId, @@ -53,5 +57,7 @@ fun aUserProfileState( startDmActionState = startDmActionState, displayConfirmationDialog = displayConfirmationDialog, isCurrentUser = isCurrentUser, + dmRoomId = dmRoomId, + canCall = canCall, eventSink = eventSink, ) diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt index fcc1f1debc6..3d5ae4a66c9 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt @@ -16,6 +16,7 @@ package io.element.android.features.userprofile.shared +import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets @@ -29,20 +30,14 @@ 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.shared.blockuser.BlockUserDialogs 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.ListItemStyle 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 @@ -52,11 +47,13 @@ import io.element.android.libraries.ui.strings.CommonStrings fun UserProfileView( state: UserProfileState, onShareUser: () -> Unit, - onDMStarted: (RoomId) -> Unit, + onDmStarted: (RoomId) -> Unit, + onStartCall: (RoomId) -> Unit, goBack: () -> Unit, openAvatarPreview: (username: String, url: String) -> Unit, modifier: Modifier = Modifier, ) { + BackHandler { goBack() } Scaffold( modifier = modifier, topBar = { @@ -78,12 +75,17 @@ fun UserProfileView( }, ) - UserProfileMainActionsSection(onShareUser = onShareUser) + UserProfileMainActionsSection( + isCurrentUser = state.isCurrentUser, + canCall = state.canCall, + onShareUser = onShareUser, + onStartDM = { state.eventSink(UserProfileEvents.StartDM) }, + onCall = { state.dmRoomId?.let { onStartCall(it) } } + ) Spacer(modifier = Modifier.height(26.dp)) if (!state.isCurrentUser) { - StartDMSection(onStartDMClicked = { state.eventSink(UserProfileEvents.StartDM) }) BlockUserSection(state) BlockUserDialogs(state) } @@ -94,7 +96,7 @@ fun UserProfileView( progressText = stringResource(CommonStrings.common_starting_chat), ) }, - onSuccess = onDMStarted, + onSuccess = onDmStarted, errorMessage = { stringResource(R.string.screen_start_chat_error_starting_chat) }, onRetry = { state.eventSink(UserProfileEvents.StartDM) }, onErrorDismiss = { state.eventSink(UserProfileEvents.ClearStartDMState) }, @@ -103,18 +105,6 @@ fun UserProfileView( } } -@Composable -private fun StartDMSection( - onStartDMClicked: () -> Unit, -) { - ListItem( - headlineContent = { Text(stringResource(CommonStrings.common_direct_chat)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Chat())), - style = ListItemStyle.Primary, - onClick = onStartDMClicked, - ) -} - @PreviewsDayNight @Composable internal fun UserProfileViewPreview( @@ -124,7 +114,8 @@ internal fun UserProfileViewPreview( state = state, onShareUser = {}, goBack = {}, - onDMStarted = {}, + onDmStarted = {}, + onStartCall = {}, openAvatarPreview = { _, _ -> } ) } diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt new file mode 100644 index 00000000000..6cc5e229e5c --- /dev/null +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.userprofile + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.userprofile.shared.R +import io.element.android.features.userprofile.shared.UserProfileEvents +import io.element.android.features.userprofile.shared.UserProfileState +import io.element.android.features.userprofile.shared.UserProfileView +import io.element.android.features.userprofile.shared.aUserProfileState +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.ensureCalledOnceWithParam +import io.element.android.tests.testutils.ensureCalledOnceWithTwoParams +import io.element.android.tests.testutils.pressBack +import io.element.android.tests.testutils.pressBackKey +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class UserProfileViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `on back key press - the expected callback is called`() = runTest { + ensureCalledOnce { callback -> + rule.setUserProfileView( + goBack = callback, + ) + rule.pressBackKey() + } + } + + @Test + fun `on back button click - the expected callback is called`() = runTest { + ensureCalledOnce { callback -> + rule.setUserProfileView( + goBack = callback, + ) + rule.pressBack() + } + } + + @Test + fun `on avatar clicked - the expected callback is called`() = runTest { + ensureCalledOnceWithTwoParams(A_USER_NAME, AN_AVATAR_URL) { callback -> + rule.setUserProfileView( + state = aUserProfileState(userName = A_USER_NAME, avatarUrl = AN_AVATAR_URL), + openAvatarPreview = callback, + ) + rule.onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() + } + } + + @Test + fun `on avatar clicked with no avatar - nothing happens`() = runTest { + val callback = EnsureNeverCalledWithTwoParams() + rule.setUserProfileView( + state = aUserProfileState(userName = A_USER_NAME, avatarUrl = null), + openAvatarPreview = callback, + ) + rule.onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() + } + + @Test + fun `on Share clicked - the expected callback is called`() = runTest { + ensureCalledOnce { callback -> + rule.setUserProfileView( + onShareUser = callback, + ) + rule.clickOn(CommonStrings.action_share) + } + } + + @Test + fun `on Message clicked - the StartDm event is emitted`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + dmRoomId = A_ROOM_ID, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_message) + eventsRecorder.assertSingle(UserProfileEvents.StartDM) + } + + @Test + fun `on Call clicked - the expected callback is called`() = runTest { + ensureCalledOnceWithParam(A_ROOM_ID) { callback -> + rule.setUserProfileView( + state = aUserProfileState( + dmRoomId = A_ROOM_ID, + canCall = true, + ), + onStartCall = callback, + ) + rule.clickOn(CommonStrings.action_call) + } + } + + @Test + fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_dm_details_block_user) + eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = true)) + } + + @Test + fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_dm_details_block_alert_action) + eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = false)) + } + + @Test + fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + } + + @Test + fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + isBlocked = AsyncData.Success(true), + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_dm_details_unblock_user) + eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = true)) + } + + @Test + fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + isBlocked = AsyncData.Success(true), + displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_dm_details_unblock_alert_action) + eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = false)) + } + + @Test + fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runTest { + val eventsRecorder = EventsRecorder() + rule.setUserProfileView( + state = aUserProfileState( + isBlocked = AsyncData.Success(true), + displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) + } +} + +private fun AndroidComposeTestRule.setUserProfileView( + state: UserProfileState = aUserProfileState( + eventSink = EventsRecorder(expectEvents = false), + ), + onShareUser: () -> Unit = EnsureNeverCalled(), + onDmStarted: (RoomId) -> Unit = EnsureNeverCalledWithParam(), + onStartCall: (RoomId) -> Unit = EnsureNeverCalledWithParam(), + goBack: () -> Unit = EnsureNeverCalled(), + openAvatarPreview: (String, String) -> Unit = EnsureNeverCalledWithTwoParams(), +) { + setContent { + UserProfileView( + state = state, + onShareUser = onShareUser, + onDmStarted = onDmStarted, + onStartCall = onStartCall, + goBack = goBack, + openAvatarPreview = openAvatarPreview, + ) + } +} diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 4f88ddc05ca..e667873318a 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -73,6 +73,7 @@ "Load more" "Manage account" "Manage devices" + "Message" "Next" "No" "Not now" diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt index ba0b2dbf889..aa69c919ef9 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt @@ -105,3 +105,9 @@ fun ensureCalledOnceWithParam(param: T, block: (callback: EnsureCalledOnc block(callback) callback.assertSuccess() } + +fun ensureCalledOnceWithTwoParams(param1: P1, param2: P2, block: (callback: EnsureCalledOnceWithTwoParams) -> Unit) { + val callback = EnsureCalledOnceWithTwoParams(param1, param2) + block(callback) + callback.assertSuccess() +} diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_0,NEXUS_5,1.0,en].png index 3d4213060ee..8b6bb82e6e4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fc6369578a3684c1a88e904848bcb16a7e4cde46c887938db909563c6c54d2d -size 23031 +oid sha256:96414480d9e361319abf7543324927300f841633f628da68901d6291b8cfd675 +size 22667 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_1,NEXUS_5,1.0,en].png index 468d483d650..03a9a3b1e48 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59e3eccfa962e9d667aaa326ab3258214334db558983bc251543835585d78453 -size 20930 +oid sha256:7388e54cc1fccc87a3257b099aa832f851aabace145cc09f965431653fc0cf35 +size 20661 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_2,NEXUS_5,1.0,en].png index 5f22b0b2a8c..d8f05791b5a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07418c91e8efee98f7b9e7d069bf3aef2b674164dad0795719f97ad6b214225b -size 23550 +oid sha256:a5ca4c3f9c7376656357f07a6b1777bca92808eeb972f9f7d03bdac89a6c5867 +size 22987 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_3,NEXUS_5,1.0,en].png index 099c6f745d0..b8674d2c529 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90fe61eb831db5e1f1eb213fcdd690f06f2e547b8ad864680288d529d379d30e -size 43651 +oid sha256:2348853735daae624c99658d832382fd834a770b40fde0004089f6378ecb954c +size 41498 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_4,NEXUS_5,1.0,en].png index 940ff8e9504..99af2f113ca 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6b0328e4f5a06b59ac5b73f4230bd950bed6847af275b9e6851ffdb9403742b -size 35523 +oid sha256:364172de34c530ce2cb4799e0b5ac5aa810068794bab10c9f04f72f709ebd8fa +size 32482 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_5,NEXUS_5,1.0,en].png index 5e055eea74f..ab5bb1fa95b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7536568491a41526b48354b60db41e47d40b6677e5b38f464c57715499514171 -size 24193 +oid sha256:1a956800e49a48da34a49f7ec4c2647641a1c64af70e4ccbcdbdac0fb0cc5616 +size 23513 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_6,NEXUS_5,1.0,en].png index 55525becf3f..eabbc94384b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:902bd8ddad85ee2ea52898a910baf0c710880d94c36f56e22916bf4d15f4469c -size 25864 +oid sha256:01308cc0d4361e2685a16ce02526a9efdc4f81b86fdb982b016e090bcafa9bbb +size 23486 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..e6ff40b44c2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab8949d70455a9b3ef5d6414b788cb1cddec95fc238df8277dfd41c405a4060c +size 23581 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..8b6bb82e6e4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Day-0_1_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96414480d9e361319abf7543324927300f841633f628da68901d6291b8cfd675 +size 22667 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_0,NEXUS_5,1.0,en].png index 93516a3dd0d..c053e1b2c6a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1744d73991a0987c53d214041a26eaf7a3ce8a2ff4c90739f65f8641c4af921d -size 22351 +oid sha256:4a6807e02706f29b5b748cfbbb13f9e1437e38438da1c81fa568709e5f88d7a4 +size 22041 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_1,NEXUS_5,1.0,en].png index 6d992d9f945..2ed0e1891d6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4598d748f84146e66b547a5d9310c7e67477e1eb66d1c58bfb23f28807b7f54d -size 20263 +oid sha256:b9b5cf8ae7992a8ef83056daa2fb8ebff0c60310ec675efa0e45faa58c0e58ad +size 20049 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_2,NEXUS_5,1.0,en].png index b5e74695fec..855c0f143bd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d448afa778c03771454d5493f15748dce3eeb7a7b35cd33cb38bb5a06646f5d -size 22550 +oid sha256:e256df9ae5a854a906a21868ecdf21a5d2ce37577a69a1da557949562d1da45d +size 22193 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_3,NEXUS_5,1.0,en].png index 82575f3676d..c734b94bf7e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e000e964df93279e8325219a8a9ae74d1f66c533d418fabb88a208bc1165dbed -size 38724 +oid sha256:aefcccd96088200097cd12a934b27df29819044ab315f3e0a3e1df9228336669 +size 36801 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_4,NEXUS_5,1.0,en].png index 4f2c4d458c6..16e8b986ac9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf1d5e3367a9684cd4fd8ffebd4c38475fad0a6a6fa122df19281c1a3bf7349f -size 30814 +oid sha256:561230a37864cced2286168b6bfe7c9e461177c056386dc08e538643d58b15a6 +size 28162 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_5,NEXUS_5,1.0,en].png index 3e589cdb18f..f36f7b2281d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15c3812a12a76e5701bc1f487703ee32d6920f3c2d790f5b1d1a4b468da28000 -size 23047 +oid sha256:125d38504d4500506fcb9d85eb9ce057620e683674efb2e6b33356764a14a1af +size 22677 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_6,NEXUS_5,1.0,en].png index f1278e87d78..f9af24ede2e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3fefee021f7c3bc6a8b950a129fd7549b05416ff72b32b2b24a4317555f43d8 -size 22916 +oid sha256:5600ca86ffac9b33c320432dfb75b6242d7726ef39de0bc0e227b166565fa91f +size 20622 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..a14e3941de7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cc9c9f6cdf542c4817dfcf54f43ebd3e8c7fc47d36a50e60be28642dfbdf8ae +size 22868 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..c053e1b2c6a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.userprofile.shared_UserProfileView_null_UserProfileView-Night-0_2_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a6807e02706f29b5b748cfbbb13f9e1437e38438da1c81fa568709e5f88d7a4 +size 22041