diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 5852103b92..ad9bb7bfdf 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -29,12 +29,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager @@ -63,9 +61,7 @@ import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList import io.element.android.libraries.matrix.ui.components.UnsavedAvatar import io.element.android.libraries.permissions.api.PermissionsView import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterialApi::class) @Composable fun ConfigureRoomView( state: ConfigureRoomState, @@ -73,17 +69,12 @@ fun ConfigureRoomView( onRoomCreated: (RoomId) -> Unit, modifier: Modifier = Modifier, ) { - val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current - val itemActionsBottomSheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - ) + val isAvatarActionsSheetVisible = remember { mutableStateOf(false) } fun onAvatarClicked() { focusManager.clearFocus() - coroutineScope.launch { - itemActionsBottomSheetState.show() - } + isAvatarActionsSheetVisible.value = true } Scaffold( @@ -143,7 +134,8 @@ fun ConfigureRoomView( AvatarActionBottomSheet( actions = state.avatarActions, - modalBottomSheetState = itemActionsBottomSheetState, + isVisible = isAvatarActionsSheetVisible.value, + onDismiss = { isAvatarActionsSheetVisible.value = false }, onActionSelected = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) } ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt index c37dba8c3a..d85936d5d8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList data class EditUserProfileState( - val userId: UserId?, + val userId: UserId, val displayName: String, val userAvatarUrl: Uri?, val avatarActions: ImmutableList, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index a0d0fcfad6..389d7c5b08 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -25,12 +25,10 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager @@ -57,9 +55,8 @@ import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.permissions.api.PermissionsView import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun EditUserProfileView( state: EditUserProfileState, @@ -67,17 +64,12 @@ fun EditUserProfileView( onProfileEdited: () -> Unit, modifier: Modifier = Modifier, ) { - val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current - val itemActionsBottomSheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - ) + val isAvatarActionsSheetVisible = remember { mutableStateOf(false) } fun onAvatarClicked() { focusManager.clearFocus() - coroutineScope.launch { - itemActionsBottomSheetState.show() - } + isAvatarActionsSheetVisible.value = true } Scaffold( @@ -114,7 +106,7 @@ fun EditUserProfileView( ) { Spacer(modifier = Modifier.height(24.dp)) EditableAvatarView( - userId = state.userId?.value, + matrixId = state.userId.value, displayName = state.displayName, avatarUrl = state.userAvatarUrl, avatarSize = AvatarSize.RoomHeader, @@ -122,14 +114,12 @@ fun EditUserProfileView( modifier = Modifier.align(Alignment.CenterHorizontally), ) Spacer(modifier = Modifier.height(16.dp)) - state.userId?.let { - Text( - modifier = Modifier.fillMaxWidth(), - text = it.value, - style = ElementTheme.typography.fontBodyLgRegular, - textAlign = TextAlign.Center, - ) - } + Text( + modifier = Modifier.fillMaxWidth(), + text = state.userId.value, + style = ElementTheme.typography.fontBodyLgRegular, + textAlign = TextAlign.Center, + ) Spacer(modifier = Modifier.height(40.dp)) LabelledOutlinedTextField( label = stringResource(R.string.screen_edit_profile_display_name), @@ -142,7 +132,8 @@ fun EditUserProfileView( AvatarActionBottomSheet( actions = state.avatarActions, - modalBottomSheetState = itemActionsBottomSheetState, + isVisible = isAvatarActionsSheetVisible.value, + onDismiss = { isAvatarActionsSheetVisible.value = false }, onActionSelected = { state.eventSink(EditUserProfileEvents.HandleAvatarAction(it)) } ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt index 6213f2cf1a..67d333c41d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt @@ -37,6 +37,9 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.matrix.ui.room.avatarUrl +import io.element.android.libraries.matrix.ui.room.rawName +import io.element.android.libraries.matrix.ui.room.topic import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.permissions.api.PermissionsEvents @@ -61,23 +64,35 @@ class RoomDetailsEditPresenter @Inject constructor( val cameraPermissionState = cameraPermissionPresenter.present() val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState() - // Since there is no way to obtain the new avatar uri after uploading a new avatar, - // just erase the local value when the room field has changed - var roomAvatarUri by rememberSaveable(room.avatarUrl) { mutableStateOf(room.avatarUrl?.toUri()) } + val roomAvatarUri = room.avatarUrl()?.toUri() + var roomAvatarUriEdited by rememberSaveable { mutableStateOf(null) } + LaunchedEffect(roomAvatarUri) { + // Every time the roomAvatar change (from sync), we can set the new avatar. + roomAvatarUriEdited = roomAvatarUri + } - var roomName by rememberSaveable { mutableStateOf(room.displayName.trim()) } - var roomTopic by rememberSaveable { mutableStateOf(room.topic?.trim()) } + val roomRawNameTrimmed = room.rawName().orEmpty().trim() + var roomRawNameEdited by rememberSaveable { mutableStateOf("") } + LaunchedEffect(roomRawNameTrimmed) { + // Every time the rawName change (from sync), we can set the new name. + roomRawNameEdited = roomRawNameTrimmed + } + val roomTopicTrimmed = room.topic().orEmpty().trim() + var roomTopicEdited by rememberSaveable { mutableStateOf("") } + LaunchedEffect(roomTopicTrimmed) { + // Every time the topic change (from sync), we can set the new topic. + roomTopicEdited = roomTopicTrimmed + } val saveButtonEnabled by remember( - roomSyncUpdateFlow.value, - roomName, - roomTopic, + roomRawNameTrimmed, + roomTopicTrimmed, roomAvatarUri, ) { derivedStateOf { - roomAvatarUri?.toString()?.trim() != room.avatarUrl?.toUri()?.toString()?.trim() || - roomName.trim() != room.displayName.trim() || - roomTopic.orEmpty().trim() != room.topic.orEmpty().trim() + roomRawNameTrimmed != roomRawNameEdited.trim() || + roomTopicTrimmed != roomTopicEdited.trim() || + roomAvatarUri != roomAvatarUriEdited } } @@ -85,17 +100,17 @@ class RoomDetailsEditPresenter @Inject constructor( var canChangeTopic by remember { mutableStateOf(false) } var canChangeAvatar by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { + LaunchedEffect(roomSyncUpdateFlow.value) { canChangeName = room.canSendState(StateEventType.ROOM_NAME).getOrElse { false } canChangeTopic = room.canSendState(StateEventType.ROOM_TOPIC).getOrElse { false } canChangeAvatar = room.canSendState(StateEventType.ROOM_AVATAR).getOrElse { false } } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( - onResult = { uri -> if (uri != null) roomAvatarUri = uri } + onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } ) val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( - onResult = { uri -> if (uri != null) roomAvatarUri = uri } + onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } ) LaunchedEffect(cameraPermissionState.permissionGranted) { @@ -105,12 +120,12 @@ class RoomDetailsEditPresenter @Inject constructor( } } - val avatarActions by remember(roomAvatarUri) { + val avatarActions by remember(roomAvatarUriEdited) { derivedStateOf { listOfNotNull( AvatarAction.TakePhoto, AvatarAction.ChoosePhoto, - AvatarAction.Remove.takeIf { roomAvatarUri != null }, + AvatarAction.Remove.takeIf { roomAvatarUriEdited != null }, ).toImmutableList() } } @@ -119,7 +134,15 @@ class RoomDetailsEditPresenter @Inject constructor( val localCoroutineScope = rememberCoroutineScope() fun handleEvents(event: RoomDetailsEditEvents) { when (event) { - is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges(roomName, roomTopic, roomAvatarUri, saveAction) + is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges( + currentNameTrimmed = roomRawNameTrimmed, + newNameTrimmed = roomRawNameEdited.trim(), + currentTopicTrimmed = roomTopicTrimmed, + newTopicTrimmed = roomTopicEdited.trim(), + currentAvatar = roomAvatarUri, + newAvatarUri = roomAvatarUriEdited, + action = saveAction, + ) is RoomDetailsEditEvents.HandleAvatarAction -> { when (event.action) { AvatarAction.ChoosePhoto -> galleryImagePicker.launch() @@ -129,23 +152,23 @@ class RoomDetailsEditPresenter @Inject constructor( pendingPermissionRequest = true cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) } - AvatarAction.Remove -> roomAvatarUri = null + AvatarAction.Remove -> roomAvatarUriEdited = null } } - is RoomDetailsEditEvents.UpdateRoomName -> roomName = event.name - is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopic = event.topic.takeUnless { it.isEmpty() } + is RoomDetailsEditEvents.UpdateRoomName -> roomRawNameEdited = event.name + is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopicEdited = event.topic RoomDetailsEditEvents.CancelSaveChanges -> saveAction.value = AsyncAction.Uninitialized } } return RoomDetailsEditState( - roomId = room.roomId.value, - roomName = roomName, + roomId = room.roomId, + roomRawName = roomRawNameEdited, canChangeName = canChangeName, - roomTopic = roomTopic.orEmpty(), + roomTopic = roomTopicEdited, canChangeTopic = canChangeTopic, - roomAvatarUrl = roomAvatarUri, + roomAvatarUrl = roomAvatarUriEdited, canChangeAvatar = canChangeAvatar, avatarActions = avatarActions, saveButtonEnabled = saveButtonEnabled, @@ -156,25 +179,28 @@ class RoomDetailsEditPresenter @Inject constructor( } private fun CoroutineScope.saveChanges( - name: String, - topic: String?, - avatarUri: Uri?, + currentNameTrimmed: String, + newNameTrimmed: String, + currentTopicTrimmed: String, + newTopicTrimmed: String, + currentAvatar: Uri?, + newAvatarUri: Uri?, action: MutableState>, ) = launch { val results = mutableListOf>() suspend { - if (topic.orEmpty().trim() != room.topic.orEmpty().trim()) { - results.add(room.setTopic(topic.orEmpty()).onFailure { + if (newTopicTrimmed != currentTopicTrimmed) { + results.add(room.setTopic(newTopicTrimmed).onFailure { Timber.e(it, "Failed to set room topic") }) } - if (name.isNotEmpty() && name.trim() != room.displayName.trim()) { - results.add(room.setName(name).onFailure { + if (newNameTrimmed.isNotEmpty() && newNameTrimmed != currentNameTrimmed) { + results.add(room.setName(newNameTrimmed).onFailure { Timber.e(it, "Failed to set room name") }) } - if (avatarUri?.toString()?.trim() != room.avatarUrl?.trim()) { - results.add(updateAvatar(avatarUri).onFailure { + if (newAvatarUri != currentAvatar) { + results.add(updateAvatar(newAvatarUri).onFailure { Timber.e(it, "Failed to update avatar") }) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt index d85450b59f..ec74905e38 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt @@ -18,13 +18,15 @@ package io.element.android.features.roomdetails.impl.edit import android.net.Uri import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState import kotlinx.collections.immutable.ImmutableList data class RoomDetailsEditState( - val roomId: String, - val roomName: String, + val roomId: RoomId, + /** The raw room name (i.e. the room name from the state event `m.room.name`), not the display name. */ + val roomRawName: String, val canChangeName: Boolean, val roomTopic: String, val canChangeTopic: Boolean, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt index d70c83b9a0..abf4f64b69 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt @@ -19,33 +19,50 @@ package io.element.android.features.roomdetails.impl.edit import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.aPermissionsState -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList open class RoomDetailsEditStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aRoomDetailsEditState(), - aRoomDetailsEditState().copy(roomTopic = ""), - aRoomDetailsEditState().copy(roomAvatarUrl = Uri.parse("example://uri")), - aRoomDetailsEditState().copy(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false), - aRoomDetailsEditState().copy(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false), - aRoomDetailsEditState().copy(saveAction = AsyncAction.Loading), - aRoomDetailsEditState().copy(saveAction = AsyncAction.Failure(Throwable("Whelp"))) + aRoomDetailsEditState(roomTopic = ""), + aRoomDetailsEditState(roomRawName = ""), + aRoomDetailsEditState(roomAvatarUrl = Uri.parse("example://uri")), + aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false), + aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false), + aRoomDetailsEditState(saveAction = AsyncAction.Loading), + aRoomDetailsEditState(saveAction = AsyncAction.Failure(Throwable("Whelp"))), ) } -fun aRoomDetailsEditState() = RoomDetailsEditState( - roomId = "a room id", - roomName = "Marketing", - canChangeName = true, - roomTopic = "a room topic that is quite long so should wrap onto multiple lines", - canChangeTopic = true, - roomAvatarUrl = null, - canChangeAvatar = true, - avatarActions = persistentListOf(), - saveButtonEnabled = true, - saveAction = AsyncAction.Uninitialized, - cameraPermissionState = aPermissionsState(showDialog = false), - eventSink = {} +fun aRoomDetailsEditState( + roomId: RoomId = RoomId("!aRoomId:aDomain"), + roomRawName: String = "Marketing", + canChangeName: Boolean = true, + roomTopic: String = "a room topic that is quite long so should wrap onto multiple lines", + canChangeTopic: Boolean = true, + roomAvatarUrl: Uri? = null, + canChangeAvatar: Boolean = true, + avatarActions: List = emptyList(), + saveButtonEnabled: Boolean = true, + saveAction: AsyncAction = AsyncAction.Uninitialized, + cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false), + eventSink: (RoomDetailsEditEvents) -> Unit = {}, +) = RoomDetailsEditState( + roomId = roomId, + roomRawName = roomRawName, + canChangeName = canChangeName, + roomTopic = roomTopic, + canChangeTopic = canChangeTopic, + roomAvatarUrl = roomAvatarUrl, + canChangeAvatar = canChangeAvatar, + avatarActions = avatarActions.toImmutableList(), + saveButtonEnabled = saveButtonEnabled, + saveAction = saveAction, + cameraPermissionState = cameraPermissionState, + eventSink = eventSink, ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index b93bbd1f47..e0da0d2f19 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) +@file:OptIn(ExperimentalMaterial3Api::class) package io.element.android.features.roomdetails.impl.edit @@ -29,13 +29,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -61,9 +59,7 @@ import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.EditableAvatarView import io.element.android.libraries.permissions.api.PermissionsView import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable fun RoomDetailsEditView( state: RoomDetailsEditState, @@ -71,17 +67,12 @@ fun RoomDetailsEditView( onRoomEdited: () -> Unit, modifier: Modifier = Modifier, ) { - val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current - val itemActionsBottomSheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - ) + val isAvatarActionsSheetVisible = remember { mutableStateOf(false) } fun onAvatarClicked() { focusManager.clearFocus() - coroutineScope.launch { - itemActionsBottomSheetState.show() - } + isAvatarActionsSheetVisible.value = true } Scaffold( @@ -118,8 +109,9 @@ fun RoomDetailsEditView( ) { Spacer(modifier = Modifier.height(24.dp)) EditableAvatarView( - userId = state.roomId, - displayName = state.roomName, + matrixId = state.roomId.value, + // As per Element Web, we use the raw name for the avatar as well + displayName = state.roomRawName, avatarUrl = state.roomAvatarUrl, avatarSize = AvatarSize.EditRoomDetails, onAvatarClicked = ::onAvatarClicked, @@ -130,7 +122,7 @@ fun RoomDetailsEditView( if (state.canChangeName) { LabelledTextField( label = stringResource(id = R.string.screen_room_details_room_name_label), - value = state.roomName, + value = state.roomRawName, placeholder = stringResource(CommonStrings.common_room_name_placeholder), singleLine = true, onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomName(it)) }, @@ -138,7 +130,7 @@ fun RoomDetailsEditView( } else { LabelledReadOnlyField( title = stringResource(R.string.screen_room_details_room_name_label), - value = state.roomName + value = state.roomRawName ) } @@ -166,7 +158,8 @@ fun RoomDetailsEditView( AvatarActionBottomSheet( actions = state.avatarActions, - modalBottomSheetState = itemActionsBottomSheetState, + isVisible = isAvatarActionsSheetVisible.value, + onDismiss = { isAvatarActionsSheetVisible.value = false }, onActionSelected = { state.eventSink(RoomDetailsEditEvents.HandleAvatarAction(it)) } ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt new file mode 100644 index 0000000000..cffd2ae1f1 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/MatrixRoomFixture.kt @@ -0,0 +1,59 @@ +/* + * 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.roomdetails + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo + +fun aMatrixRoom( + roomId: RoomId = A_ROOM_ID, + displayName: String = A_ROOM_NAME, + rawName: String? = displayName, + topic: String? = "A topic", + avatarUrl: String? = "https://matrix.org/avatar.jpg", + isEncrypted: Boolean = true, + isPublic: Boolean = true, + isDirect: Boolean = false, + notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), + emitRoomInfo: Boolean = false, +) = FakeMatrixRoom( + roomId = roomId, + displayName = displayName, + topic = topic, + avatarUrl = avatarUrl, + isEncrypted = isEncrypted, + isPublic = isPublic, + isDirect = isDirect, + notificationSettingsService = notificationSettingsService +).apply { + if (emitRoomInfo) { + givenRoomInfo( + aRoomInfo( + name = displayName, + rawName = rawName, + topic = topic, + avatarUrl = avatarUrl, + isDirect = isDirect, + isPublic = isPublic, + ) + ) + } +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 998193690e..857422ca51 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -37,14 +37,11 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.StateEventType -import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -473,23 +470,3 @@ class RoomDetailsPresenterTests { } } } - -fun aMatrixRoom( - roomId: RoomId = A_ROOM_ID, - displayName: String = A_ROOM_NAME, - topic: String? = "A topic", - avatarUrl: String? = "https://matrix.org/avatar.jpg", - isEncrypted: Boolean = true, - isPublic: Boolean = true, - isDirect: Boolean = false, - notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService() -) = FakeMatrixRoom( - roomId = roomId, - displayName = displayName, - topic = topic, - avatarUrl = avatarUrl, - isEncrypted = isEncrypted, - isPublic = isPublic, - isDirect = isDirect, - notificationSettingsService = notificationSettingsService -) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt index 8b0dcd5b0a..f287fe4ab4 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.edit import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow +import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.aMatrixRoom @@ -28,6 +29,8 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.api.MediaUploadInfo @@ -90,15 +93,19 @@ class RoomDetailsEditPresenterTest { @Test fun `present - initial state is created from room info`() = runTest { - val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL) + val room = aMatrixRoom( + avatarUrl = AN_AVATAR_URL, + displayName = A_ROOM_NAME, + rawName = A_ROOM_RAW_NAME, + emitRoomInfo = true, + ) val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - assertThat(initialState.roomId).isEqualTo(room.roomId.value) - assertThat(initialState.roomName).isEqualTo(room.displayName) + val initialState = awaitFirstItem() + assertThat(initialState.roomId).isEqualTo(room.roomId) + assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME) assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) assertThat(initialState.roomTopic).isEqualTo(room.topic.orEmpty()) assertThat(initialState.avatarActions).containsExactly( @@ -119,7 +126,6 @@ class RoomDetailsEditPresenterTest { givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) } val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -128,7 +134,6 @@ class RoomDetailsEditPresenterTest { assertThat(initialState.canChangeName).isFalse() assertThat(initialState.canChangeAvatar).isFalse() assertThat(initialState.canChangeTopic).isFalse() - // When the asynchronous check completes, the single field we can edit is true val settledState = awaitItem() assertThat(settledState.canChangeName).isTrue() @@ -145,7 +150,6 @@ class RoomDetailsEditPresenterTest { givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) } val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -154,7 +158,6 @@ class RoomDetailsEditPresenterTest { assertThat(initialState.canChangeName).isFalse() assertThat(initialState.canChangeAvatar).isFalse() assertThat(initialState.canChangeTopic).isFalse() - // When the asynchronous check completes, the single field we can edit is true val settledState = awaitItem() assertThat(settledState.canChangeName).isFalse() @@ -171,7 +174,6 @@ class RoomDetailsEditPresenterTest { givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) } val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -180,7 +182,6 @@ class RoomDetailsEditPresenterTest { assertThat(initialState.canChangeName).isFalse() assertThat(initialState.canChangeAvatar).isFalse() assertThat(initialState.canChangeTopic).isFalse() - // When the asynchronous check completes, the single field we can edit is true val settledState = awaitItem() assertThat(settledState.canChangeName).isFalse() @@ -191,42 +192,42 @@ class RoomDetailsEditPresenterTest { @Test fun `present - updates state in response to changes`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ) val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.roomTopic).isEqualTo("My topic") - assertThat(initialState.roomName).isEqualTo("Name") + assertThat(initialState.roomRawName).isEqualTo("Name") assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { assertThat(roomTopic).isEqualTo("My topic") - assertThat(roomName).isEqualTo("Name II") + assertThat(roomRawName).isEqualTo("Name II") assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) } - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name III")) awaitItem().apply { assertThat(roomTopic).isEqualTo("My topic") - assertThat(roomName).isEqualTo("Name III") + assertThat(roomRawName).isEqualTo("Name III") assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) } - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { assertThat(roomTopic).isEqualTo("Another topic") - assertThat(roomName).isEqualTo("Name III") + assertThat(roomRawName).isEqualTo("Name III") assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) } - initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { assertThat(roomTopic).isEqualTo("Another topic") - assertThat(roomName).isEqualTo("Name III") + assertThat(roomRawName).isEqualTo("Name III") assertThat(roomAvatarUrl).isNull() } } @@ -234,18 +235,19 @@ class RoomDetailsEditPresenterTest { @Test fun `present - obtains avatar uris from gallery`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ) fakePickerProvider.givenResult(anotherAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) - initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri) @@ -255,19 +257,22 @@ class RoomDetailsEditPresenterTest { @Test fun `present - obtains avatar uris from camera`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ) fakePickerProvider.givenResult(anotherAvatarUri) val fakePermissionsPresenter = FakePermissionsPresenter() val presenter = createRoomDetailsEditPresenter( room = room, permissionsPresenter = fakePermissionsPresenter, ) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) assertThat(initialState.cameraPermissionState.permissionGranted).isFalse() initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) @@ -288,48 +293,44 @@ class RoomDetailsEditPresenterTest { @Test fun `present - updates save button state`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ) fakePickerProvider.givenResult(roomAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.saveButtonEnabled).isFalse() - // Once a change is made, the save button is enabled initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // If it's reverted then the save disables again initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name")) awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } - // Make a change... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // Revert it... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("My topic")) awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } - // Make a change... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // Revert it... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { @@ -340,48 +341,44 @@ class RoomDetailsEditPresenterTest { @Test fun `present - updates save button state when initial values are null`() = runTest { - val room = aMatrixRoom(topic = null, displayName = "fallback", avatarUrl = null) - + val room = aMatrixRoom( + topic = null, + displayName = "fallback", + avatarUrl = null, + emitRoomInfo = true, + ) fakePickerProvider.givenResult(roomAvatarUri) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.saveButtonEnabled).isFalse() - // Once a change is made, the save button is enabled initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // If it's reverted then the save disables again initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("fallback")) awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } - // Make a change... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // Revert it... initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) awaitItem().apply { assertThat(saveButtonEnabled).isFalse() } - // Make a change... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) awaitItem().apply { assertThat(saveButtonEnabled).isTrue() } - // Revert it... initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) awaitItem().apply { @@ -392,15 +389,17 @@ class RoomDetailsEditPresenterTest { @Test fun `present - save changes room details if different`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ) val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - + val initialState = awaitFirstItem() initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("New name")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("New topic")) initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) @@ -410,31 +409,24 @@ class RoomDetailsEditPresenterTest { assertThat(room.newTopic).isEqualTo("New topic") assertThat(room.newAvatarData).isNull() assertThat(room.removedAvatar).isTrue() - - cancelAndIgnoreRemainingEvents() } } @Test fun `present - save doesn't change room details if they're the same trimmed`() = runTest { val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name ")) initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic ")) initialState.eventSink(RoomDetailsEditEvents.Save) - assertThat(room.newName).isNull() assertThat(room.newTopic).isNull() assertThat(room.newAvatarData).isNull() assertThat(room.removedAvatar).isFalse() - cancelAndIgnoreRemainingEvents() } } @@ -442,22 +434,17 @@ class RoomDetailsEditPresenterTest { @Test fun `present - save doesn't change topic if it was unset and is now blank`() = runTest { val room = aMatrixRoom(topic = null, displayName = "Name", avatarUrl = AN_AVATAR_URL) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) initialState.eventSink(RoomDetailsEditEvents.Save) - assertThat(room.newName).isNull() assertThat(room.newTopic).isNull() assertThat(room.newAvatarData).isNull() assertThat(room.removedAvatar).isFalse() - cancelAndIgnoreRemainingEvents() } } @@ -465,22 +452,17 @@ class RoomDetailsEditPresenterTest { @Test fun `present - save doesn't change name if it's now empty`() = runTest { val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("")) initialState.eventSink(RoomDetailsEditEvents.Save) - assertThat(room.newName).isNull() assertThat(room.newTopic).isNull() assertThat(room.newAvatarData).isNull() assertThat(room.removedAvatar).isFalse() - cancelAndIgnoreRemainingEvents() } } @@ -488,20 +470,15 @@ class RoomDetailsEditPresenterTest { @Test fun `present - save processes and sets avatar when processor returns successfully`() = runTest { val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - givenPickerReturnsFile() - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(3) - assertThat(room.newName).isNull() assertThat(room.newTopic).isNull() assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents) @@ -512,89 +489,92 @@ class RoomDetailsEditPresenterTest { @Test fun `present - save does not set avatar data if processor fails`() = runTest { val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL) - fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(2) - assertThat(room.newName).isNull() assertThat(room.newTopic).isNull() assertThat(room.newAvatarData).isNull() assertThat(room.removedAvatar).isFalse() - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) } } @Test fun `present - sets save action to failure if name update fails`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ).apply { givenSetNameResult(Result.failure(Throwable("!"))) } - saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name")) } @Test fun `present - sets save action to failure if topic update fails`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ).apply { givenSetTopicResult(Result.failure(Throwable("!"))) } - saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic")) } @Test fun `present - sets save action to failure if removing avatar fails`() = runTest { - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ).apply { givenRemoveAvatarResult(Result.failure(Throwable("!"))) } - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) } @Test fun `present - sets save action to failure if setting avatar fails`() = runTest { givenPickerReturnsFile() - - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { + val room = aMatrixRoom( + topic = "My topic", + displayName = "Name", + avatarUrl = AN_AVATAR_URL, + emitRoomInfo = true, + ).apply { givenUpdateAvatarResult(Result.failure(Throwable("!"))) } - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) } @Test fun `present - CancelSaveChanges resets save action state`() = runTest { givenPickerReturnsFile() - val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply { givenSetTopicResult(Result.failure(Throwable("!"))) } - val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo")) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(2) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) - initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } @@ -602,16 +582,13 @@ class RoomDetailsEditPresenterTest { private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) { val presenter = createRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - + val initialState = awaitFirstItem() initialState.eventSink(event) initialState.eventSink(RoomDetailsEditEvents.Save) skipItems(1) - assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java) } @@ -622,7 +599,6 @@ class RoomDetailsEditPresenterTest { val processedFile: File = mockk { every { readBytes() } returns fakeFileContents } - fakePickerProvider.givenResult(anotherAvatarUri) fakeMediaPreProcessor.givenResult( Result.success( @@ -638,3 +614,8 @@ class RoomDetailsEditPresenterTest { private const val ANOTHER_AVATAR_URL = "example://camera/foo.jpg" } } + +private suspend fun ReceiveTurbine.awaitFirstItem(): T { + skipItems(2) + return awaitItem() +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditViewTest.kt new file mode 100644 index 0000000000..4db2d9bb02 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditViewTest.kt @@ -0,0 +1,243 @@ +/* + * 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.roomdetails.edit + +import androidx.activity.ComponentActivity +import androidx.annotation.StringRes +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertHasNoClickAction +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.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditEvents +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditState +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditView +import io.element.android.features.roomdetails.impl.edit.aRoomDetailsEditState +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.ui.media.AvatarAction +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.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.pressBack +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class RoomDetailsEditViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on back invoke back callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder + ), + onBackPressed = callback, + ) + rule.pressBack() + } + } + + @Test + fun `when edition is successful, the expected callback is invoked`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + saveAction = AsyncAction.Success(Unit) + ), + onRoomEdited = callback, + ) + } + } + + @Test + fun `when name is changed, the expected Event is emitted`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + roomRawName = "Marketing", + ), + ) + rule.onNodeWithText("Marketing").assertHasClickAction() + rule.onNodeWithText("Marketing").performTextInput("A") + eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomName("AMarketing")) + } + + @Test + fun `when user cannot change name, nothing happen`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + roomRawName = "Marketing", + canChangeName = false, + ), + ) + rule.onNodeWithText("Marketing").assertHasNoClickAction() + } + + @Test + fun `when topic is changed, the expected Event is emitted`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + roomTopic = "My Topic", + ), + ) + rule.onNodeWithText("My Topic").assertHasClickAction() + rule.onNodeWithText("My Topic").performTextInput("A") + eventsRecorder.assertSingle(RoomDetailsEditEvents.UpdateRoomTopic("AMy Topic")) + } + + @Test + fun `when user cannot change topic, nothing happen`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + roomTopic = "My Topic", + canChangeTopic = false, + ), + ) + rule.onNodeWithText("My Topic").assertHasNoClickAction() + } + + @Ignore("This test is failing because the bottom sheet does not open") + @Test + fun `when avatar is changed with action to take photo, the expected Event is emitted`() { + testAvatarChange( + stringActionRes = CommonStrings.action_take_photo, + expectedEvent = RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto), + ) + } + + @Ignore("This test is failing because the bottom sheet does not open") + @Test + fun `when avatar is changed with action to choose photo, the expected Event is emitted`() { + testAvatarChange( + stringActionRes = CommonStrings.action_choose_photo, + expectedEvent = RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), + ) + } + + @Ignore("This test is failing because the bottom sheet does not open") + @Test + fun `when avatar is changed with action to remove photo, the expected Event is emitted`() { + testAvatarChange( + stringActionRes = CommonStrings.action_remove, + expectedEvent = RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), + ) + } + + private fun testAvatarChange( + @StringRes stringActionRes: Int, + expectedEvent: RoomDetailsEditEvents.HandleAvatarAction, + ) { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + ), + ) + // Open the bottom sheet + rule.onNode(hasTestTag(TestTags.editAvatar.value)).performClick() + rule.onNodeWithText(rule.activity.getString(stringActionRes)).assertExists() + rule.clickOn(stringActionRes) + eventsRecorder.assertSingle(expectedEvent) + } + + @Test + fun `when user cannot change avatar, nothing happen`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + canChangeAvatar = false, + ), + ) + rule.onNode(hasTestTag(TestTags.editAvatar.value)).performClick() + rule.onNodeWithText(rule.activity.getString(CommonStrings.action_take_photo)).assertDoesNotExist() + } + + @Test + fun `when save is clicked, the expected Event is emitted`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + saveButtonEnabled = true, + ), + ) + rule.clickOn(CommonStrings.action_save) + eventsRecorder.assertSingle(RoomDetailsEditEvents.Save) + } + + @Test + fun `when save is clicked, but nothing need to be saved, nothing happens`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + saveButtonEnabled = false, + ), + ) + rule.clickOn(CommonStrings.action_save) + } + + @Test + fun `when error is shown, closing the dialog emit the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailsEditView( + aRoomDetailsEditState( + eventSink = eventsRecorder, + saveAction = AsyncAction.Failure(Throwable("Whelp")), + ), + ) + rule.clickOn(CommonStrings.action_ok) + eventsRecorder.assertSingle(RoomDetailsEditEvents.CancelSaveChanges) + } +} + +private fun AndroidComposeTestRule.setRoomDetailsEditView( + state: RoomDetailsEditState, + onBackPressed: () -> Unit = EnsureNeverCalled(), + onRoomEdited: () -> Unit = EnsureNeverCalled(), +) { + setContent { + RoomDetailsEditView( + state = state, + onBackPressed = onBackPressed, + onRoomEdited = onRoomEdited, + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt deleted file mode 100644 index 8dd63d1cc6..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - -// This is actually expected, as we should remove this component soon and use ModalBottomSheet instead -@file:Suppress("UsingMaterialAndMaterial3Libraries") - -package io.element.android.libraries.designsystem.theme.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CornerSize -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetDefaults -import androidx.compose.material.ModalBottomSheetState -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.contentColorFor -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage -import io.element.android.libraries.designsystem.modifiers.applyIf -import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.preview.PreviewGroup - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun ModalBottomSheetLayout( - sheetContent: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), - sheetShape: Shape = MaterialTheme.shapes.large.copy(bottomStart = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)), - sheetElevation: Dp = ModalBottomSheetDefaults.Elevation, - sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface, - sheetContentColor: Color = contentColorFor(sheetBackgroundColor), - scrimColor: Color = ModalBottomSheetDefaults.scrimColor, - displayHandle: Boolean = false, - useSystemPadding: Boolean = true, - content: @Composable () -> Unit = {} -) { - androidx.compose.material.ModalBottomSheetLayout( - sheetContent = { - Column( - Modifier.fillMaxWidth() - .applyIf(useSystemPadding, ifTrue = { - navigationBarsPadding() - }) - ) { - if (displayHandle) { - Spacer(modifier = Modifier.height(16.dp)) - Box( - modifier = Modifier - .background(MaterialTheme.colorScheme.onSurfaceVariant, RoundedCornerShape(2.dp)) - .size(width = 32.dp, height = 4.dp) - .align(Alignment.CenterHorizontally), - ) - Spacer(modifier = Modifier.height(24.dp)) - } - sheetContent() - } - }, - modifier = modifier, - sheetState = sheetState, - sheetShape = sheetShape, - sheetElevation = sheetElevation, - sheetBackgroundColor = sheetBackgroundColor, - sheetContentColor = sheetContentColor, - scrimColor = scrimColor, - content = content, - ) -} - -@Preview(group = PreviewGroup.BottomSheets) -@Composable -internal fun ModalBottomSheetLayoutLightPreview() = - ElementPreviewLight { ContentToPreview() } - -@Preview(group = PreviewGroup.BottomSheets) -@Composable -internal fun ModalBottomSheetLayoutDarkPreview() = - ElementPreviewDark { ContentToPreview() } - -@OptIn(ExperimentalMaterialApi::class) -@ExcludeFromCoverage -@Composable -private fun ContentToPreview() { - ModalBottomSheetLayout( - modifier = Modifier.height(140.dp), - displayHandle = true, - sheetState = ModalBottomSheetState(ModalBottomSheetValue.Expanded, density = LocalDensity.current), - sheetContent = { - Text( - text = "Sheet Content", - modifier = Modifier - .padding(start = 16.dp, end = 16.dp, bottom = 20.dp) - .background(color = Color.Green) - ) - } - ) { - Text(text = "Content", modifier = Modifier.background(color = Color.Red)) - } -} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index eea1cc9c1b..8379d30781 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -27,7 +27,10 @@ import kotlinx.collections.immutable.ImmutableMap @Immutable data class MatrixRoomInfo( val id: RoomId, + /** The room's name from the room state event if received from sync, or one that's been computed otherwise. */ val name: String?, + /** Room name as defined by the room state event only. */ + val rawName: String?, val topic: String?, val avatarUrl: String?, val isDirect: Boolean, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index 3fe4dcf1b7..8063c67069 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -39,6 +39,7 @@ class MatrixRoomInfoMapper( return MatrixRoomInfo( id = RoomId(it.id), name = it.displayName, + rawName = it.rawName, topic = it.topic, avatarUrl = it.avatarUrl, isDirect = it.isDirect, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 9196bf364f..e507e34033 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -56,6 +56,7 @@ val A_TRANSACTION_ID = TransactionId("aTransactionId") const val A_UNIQUE_ID = "aUniqueId" const val A_ROOM_NAME = "A room name" +const val A_ROOM_RAW_NAME = "A room raw name" const val A_MESSAGE = "Hello world!" const val A_REPLY = "OK, I'll be there!" const val ANOTHER_MESSAGE = "Hello universe!" diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index fff0ce8247..a780d33ecd 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -735,6 +735,7 @@ data class EndPollInvocation( fun aRoomInfo( id: RoomId = A_ROOM_ID, name: String? = A_ROOM_NAME, + rawName: String? = name, topic: String? = "A topic", avatarUrl: String? = AN_AVATAR_URL, isDirect: Boolean = false, @@ -759,6 +760,7 @@ fun aRoomInfo( ) = MatrixRoomInfo( id = id, name = name, + rawName = rawName, topic = topic, avatarUrl = avatarUrl, isDirect = isDirect, diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index 6d955a7c05..ed7f3d068d 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.core) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.testtags) implementation(libs.coil.compose) implementation(libs.coil.gif) implementation(libs.jsoup) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index fd154928ef..84caa006d2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -14,25 +14,23 @@ * limitations under the License. */ -@file:OptIn(ExperimentalMaterialApi::class) -@file:Suppress("UsingMaterialAndMaterial3Libraries") +@file:OptIn(ExperimentalMaterial3Api::class) package io.element.android.libraries.matrix.ui.components +import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetState -import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -41,33 +39,44 @@ 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.ModalBottomSheetLayout +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.matrix.ui.media.AvatarAction import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.launch +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AvatarActionBottomSheet( actions: ImmutableList, - modalBottomSheetState: ModalBottomSheetState, + isVisible: Boolean, onActionSelected: (action: AvatarAction) -> Unit, + onDismiss: () -> Unit, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() - fun onItemActionClicked(itemAction: AvatarAction) { - onActionSelected(itemAction) - coroutineScope.launch { - modalBottomSheetState.hide() - } + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + + BackHandler(enabled = isVisible) { + sheetState.hide(coroutineScope, then = { onDismiss() }) } - ModalBottomSheetLayout( - modifier = modifier, - sheetState = modalBottomSheetState, - displayHandle = true, - sheetContent = { + fun onItemActionClicked(itemAction: AvatarAction) { + onActionSelected(itemAction) + sheetState.hide(coroutineScope, then = { onDismiss() }) + } + + if (isVisible) { + ModalBottomSheet( + onDismissRequest = { + sheetState.hide(coroutineScope, then = { onDismiss() }) + }, + modifier = modifier, + sheetState = sheetState, + ) { AvatarActionBottomSheetContent( actions = actions, onActionClicked = ::onItemActionClicked, @@ -76,7 +85,7 @@ fun AvatarActionBottomSheet( .imePadding() ) } - ) + } } @Composable @@ -115,10 +124,8 @@ private fun AvatarActionBottomSheetContent( internal fun AvatarActionBottomSheetPreview() = ElementPreview { AvatarActionBottomSheet( actions = persistentListOf(AvatarAction.TakePhoto, AvatarAction.ChoosePhoto, AvatarAction.Remove), - modalBottomSheetState = ModalBottomSheetState( - initialValue = ModalBottomSheetValue.Expanded, - density = LocalDensity.current, - ), + isVisible = true, onActionSelected = { }, + onDismiss = { }, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt index 07010329d3..ad9babb4a4 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt @@ -33,23 +33,32 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons 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 +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag @Composable fun EditableAvatarView( - userId: String?, + matrixId: String, displayName: String?, avatarUrl: Uri?, avatarSize: AvatarSize, onAvatarClicked: () -> Unit, modifier: Modifier = Modifier, ) { - Column(modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { Box( modifier = Modifier .size(avatarSize.dp) @@ -58,15 +67,14 @@ fun EditableAvatarView( onClick = onAvatarClicked, indication = rememberRipple(bounded = false), ) + .testTag(TestTags.editAvatar) ) { when (avatarUrl?.scheme) { null, "mxc" -> { - userId?.let { - Avatar( - avatarData = AvatarData(it, displayName, avatarUrl?.toString(), size = avatarSize), - modifier = Modifier.fillMaxSize(), - ) - } + Avatar( + avatarData = AvatarData(matrixId, displayName, avatarUrl?.toString(), size = avatarSize), + modifier = Modifier.fillMaxSize(), + ) } else -> { UnsavedAvatar( @@ -94,3 +102,26 @@ fun EditableAvatarView( } } } + +@PreviewsDayNight +@Composable +internal fun EditableAvatarViewPreview( + @PreviewParameter(EditableAvatarViewUriProvider::class) uri: Uri? +) = ElementPreview { + EditableAvatarView( + matrixId = "id", + displayName = "A room", + avatarUrl = uri, + avatarSize = AvatarSize.EditRoomDetails, + onAvatarClicked = {}, + ) +} + +open class EditableAvatarViewUriProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + null, + Uri.parse("mxc://matrix.org/123456"), + Uri.parse("https://example.com/avatar.jpg"), + ) +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index aa3121146a..ab3c80a6e2 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -62,3 +62,21 @@ fun MatrixRoom.isOwnUserAdmin(): Boolean { val powerLevel = roomInfo?.userPowerLevels?.get(sessionId) ?: 0L return RoomMember.Role.forPowerLevel(powerLevel) == RoomMember.Role.ADMIN } + +@Composable +fun MatrixRoom.rawName(): String? { + val roomInfo by roomInfoFlow.collectAsState(initial = null) + return roomInfo?.rawName +} + +@Composable +fun MatrixRoom.topic(): String? { + val roomInfo by roomInfoFlow.collectAsState(initial = null) + return roomInfo?.topic +} + +@Composable +fun MatrixRoom.avatarUrl(): String? { + val roomInfo by roomInfoFlow.collectAsState(initial = null) + return roomInfo?.avatarUrl +} diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 3046ba3372..5a74fa42f0 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -64,6 +64,11 @@ object TestTags { */ val memberDetailAvatar = TestTag("member_detail-avatar") + /** + * Edit avatar. + */ + val editAvatar = TestTag("edit-avatar") + /** * Welcome screen. */ diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_0,NEXUS_5,1.0,en].png index 385da4e443..8a6faf46d5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aebfeacf46df5674e4b159fd9dca029288596a2835ff953783d144cbb44c91f -size 61153 +oid sha256:ba10d755e7cefd5946a7519216a6298a1a17cbe80423bff3939e8699cc5531a6 +size 60967 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_1,NEXUS_5,1.0,en].png index 4184a2f57f..d4437de3af 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Day-3_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fe73b4f444fd4d42d99de9705ca5a73ee95cc954ab39ec874208277dac9b131 -size 84224 +oid sha256:5d2e65da53dd78db7204ffaebc8823e23f6517cdf67fcec044627a6d740361cb +size 86948 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_0,NEXUS_5,1.0,en].png index b255d112e0..dc45b621a7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8400f3b6385a3eb0096282b2e8a64d7d593ac51deb2c1a32d55acbbce430244 -size 57700 +oid sha256:3609875b66402a3cd7db798005aafb87479b07056f6e8f737cede80950a33665 +size 57693 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_1,NEXUS_5,1.0,en].png index 79cf2648fa..31fcef3505 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_ConfigureRoomView_null_ConfigureRoomView-Night-3_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:495521b5b0d4cb90ebd5be62975673965bd103c5bba12c43c3c6b2d6003acbfb -size 81045 +oid sha256:5fc8ac21d7e0e9d15b59f4bc9237dbafd974aba8bd26bd67192b04c1e3c5116c +size 83702 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png index fd83a6344d..a04d892e35 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:984d249036bb82d31cfc7d183b0a33c6419f0827e535a79390055f574e36c89a -size 22542 +oid sha256:b0ab108a6bc88c7a811e9589596aa41be765005086863a31eea31bf3cd651aad +size 22351 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png index 897e65667c..db020dc21e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bec5a025508cfff5808b3c007dcb31d780635a208d959f05891538e7dd0bba2 -size 20896 +oid sha256:82b3c70a99cad62e1cfd4cee89fa96e514f1f0b6418dcf857fcd4981e32a3ba6 +size 20885 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_0,NEXUS_5,1.0,en].png index 77f392fa53..e518d52bfc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f238794e2139509c701d363b90ae42cd0ad127e09665d82339e6d292c04c103e -size 30221 +oid sha256:f2c5b052dff4115a1b9b829e68fd46edf865994929a1e8ae0b463f168ef03907 +size 30399 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_1,NEXUS_5,1.0,en].png index cc9a457882..e4814f4c3f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8afcab5de05d583820291a8e4578a1799960cf96137bfdae46542504f6a02a06 -size 23555 +oid sha256:3c5a165e3c5c81755b739623b872bf1b1b99d6ccd8549a1c2324e59e9c421b58 +size 23688 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_2,NEXUS_5,1.0,en].png index 955974680d..afbd395bd2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c7a63959ed475c3aca89125149eb66b87702fdad9e7162fda52341bb1069eca -size 55870 +oid sha256:7eb57ad86edca68df0129f25221758647d1edab14e233e4e10f9ad1c0750e5d1 +size 31743 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_3,NEXUS_5,1.0,en].png index 9bb3821d1a..72b778a632 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0e895eee0d379ccff79d44f2ae3e954bbe23597f11086cdfb99c3b0981e392a -size 29939 +oid sha256:3fdd4a80c5a8499d3b79617c1757358de7f40a1fdb43d1c537ce4170af5efb9a +size 55730 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_4,NEXUS_5,1.0,en].png index 4a3de05872..f2fd798a84 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:acb90cc495ca721862557a0f213d80b302853309398550553bdbec0984975769 -size 29940 +oid sha256:8268170c93b3ec77e9222e926a1396f9889922fe4892d6388d2de015aee94c47 +size 30088 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_5,NEXUS_5,1.0,en].png index 79363a798a..be874e0648 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2beb630f1cafc363da0d1d7f508ad66b7738f0624d9cceefb7db35c12787b36 -size 30153 +oid sha256:2bc28e45b7d804766d5c0ce0cf217bbdb81459ad19415739835c849d8638c634 +size 30121 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_6,NEXUS_5,1.0,en].png index f801512b08..0e4dec53a5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8b761beaa0856819a39931fbd57f12a22c30b599d545f59b21b92d0d15ab0eb -size 27563 +oid sha256:f504a2811ed252564a9c14cf7d9c770d2c7993f4e839c5e4e506e555cb4258fc +size 30310 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..deb8ba493e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-3_3_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a01beba7ac836b5fdc66687298ced0a81d260fe1af3b3260b468226b90b8df48 +size 27695 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_0,NEXUS_5,1.0,en].png index 10259ac6a0..74a44c734f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cea255e3114cc3e854d9f831cd7ea646f3aa9992d3a56e81bb4a5a77a2ecf66b -size 28877 +oid sha256:f2eb8e41704492332da26e123a48f423a73104532e947246faf73604e7d54a2c +size 28844 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_1,NEXUS_5,1.0,en].png index 2d8674af42..94f6fb0da0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9eac310d1a3e7d0451ca0c21bcceeaab9c909b174a9040f31fd48a3b566673a4 -size 22568 +oid sha256:deb4b44dcd728ef7b7cef621dd67ac38087922aeeb6efffe3ab5b9ec9aa08b51 +size 22554 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_2,NEXUS_5,1.0,en].png index fbe3e62d98..19bb2dfe6a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fde4132005b1c340aff35cb703d1b2cbd43d0cdea9a552f90b7e124a909138c8 -size 54156 +oid sha256:62ee10256d36d435b0604e5014725de6eb4085d56390ece44852f0941115aebb +size 30092 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_3,NEXUS_5,1.0,en].png index 387475fac7..652078a69f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5828dfd60d097c0d571f95c977c3b573ccb0853a50dca736b0024037dbfd7d66 -size 27797 +oid sha256:df342f6121b20e20c52bf3464be4cf6880607782d046009cc8af0e6ea3ac4644 +size 54149 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_4,NEXUS_5,1.0,en].png index b10994f542..9172ec80ba 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2bd04174660f32479aff938b2e9c19a7ddf2be8b90b83f51d775bacf5a5f187 -size 28584 +oid sha256:8fd504b944225a0b95bd041bde48865cc6b25f3641c70db32aa9f30e5c532975 +size 27772 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_5,NEXUS_5,1.0,en].png index b902d94951..320e35a1d6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d6506e709ce97294d263032aa932eee53356c01b769f0ea0b6dc322ae7da393 -size 28026 +oid sha256:1da3677f03a01306116ae83dfd54e4ad29f414082be5611816411616b58bfe85 +size 28556 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_6,NEXUS_5,1.0,en].png index c8259d4147..d8c46db003 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58c14e6556999a85fde1f898c2fb562fc403f38d9c287f3db44b9f0fb29496cc -size 24255 +oid sha256:d5c26e007cdc2e7d47f79d662c64dae6ee4710e679766bc31f5eb20cfe8ddf57 +size 28023 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ec3ab4b299 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-3_4_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcb0135702ee7d8aeea60042289d30111c8b9b0d980860347f23f6ee37bd8c32 +size 24258 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutDark_null_BottomSheets_ModalBottomSheetLayoutDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutDark_null_BottomSheets_ModalBottomSheetLayoutDark_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 73cdf31525..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutDark_null_BottomSheets_ModalBottomSheetLayoutDark_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:761e809239fb9f3610e3c1760e999dd6129708e7ed41208c3eeb61f0dc0bc2bb -size 10863 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutLight_null_BottomSheets_ModalBottomSheetLayoutLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutLight_null_BottomSheets_ModalBottomSheetLayoutLight_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7c19327f0e..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_ModalBottomSheetLayoutLight_null_BottomSheets_ModalBottomSheetLayoutLight_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:898b5ca8642123df13f95b0cbdc9252d6072be9b033e9bd227c32a9703328883 -size 10972 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png index b8c565dcb9..9b1d7c4f19 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79d5851c4aac88fcf826cd9c1b114df2b82d7db132c34908f46696059404209d -size 15176 +oid sha256:e1938c8062f0a198cbf5bdb17e24f25c76dd8e8b6efcec719cc4d996b4a18b73 +size 15291 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png index ae89e9d151..6f3008e345 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c2a1aaa27a9013d7f171e8463b3b32f19ce3be4f4094657a4c3f5c1e25aa88c -size 13263 +oid sha256:f5d21fae9b198a9b7aef195938748ea5e06db9cbed397baa6cdbeb0a341393e5 +size 13485 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7bf9b7c58d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:befba1f1514fcf2f3a69b5f423fc66be7df252d133bff7fcda519c9f6acd4e8b +size 8152 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b6538451e8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f9f0c4e62431e9aa5543f28f2784ae3e245cde22ed2745f15ba3fe7be2f8ef0 +size 10287 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..918da11691 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Day-2_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcb18ec6ab064604201838b3a1497dd236731c61cc85d7be937071870dfeb5d6 +size 34082 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fb4521b9ce --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3eae0336bcaf91c668a8a26331718085b282f335ad85a0b5eaa0b2c24d890627 +size 8424 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c3060dd092 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2da272b8bc76f7d6dc3b79bdae73014d831c0fd225f830d6e15eaf4c8060737 +size 10435 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..122bb0b96c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_EditableAvatarView_null_EditableAvatarView-Night-2_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36dc08ee4cb55da521a2577715de85049ccfde5c9ed10db248fc2cd8418084f9 +size 33745 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Day-3_4_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Day-2_3_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Day-3_4_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Night-3_5_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Night-2_4_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_InviteSenderView_null_InviteSenderView-Night-3_5_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Day-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Day-5_6_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Day-4_5_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Day-5_6_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Night-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Night-5_7_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Night-4_6_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeaderPlaceholder_null_MatrixUserHeaderPlaceholder-Night-5_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-4_5_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-3_4_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-4_5_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-4_5_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-3_4_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Day-4_5_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-4_6_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-3_5_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-4_6_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-4_6_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-3_5_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserHeader_null_MatrixUserHeader-Night-4_6_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-6_7_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-5_6_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-6_7_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-6_7_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-5_6_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Day-6_7_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-6_8_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-5_7_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-6_8_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-5_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-6_8_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-5_7_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_MatrixUserRow_null_MatrixUserRow-Night-6_8_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-7_8_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-7_8_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-7_8_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-6_7_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Day-7_8_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-7_9_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-7_9_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-7_9_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-6_8_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedRoom_null_SelectedRoom-Night-7_9_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Day-9_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Day-8_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Day-9_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Night-9_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Night-8_10_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUserCannotRemove_null_SelectedUserCannotRemove-Night-9_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-8_9_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-7_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Day-8_9_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-8_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-7_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUser_null_SelectedUser-Night-8_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Day-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Day-10_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Day-9_10_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Day-10_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Night-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Night-10_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Night-9_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_SelectedUsersRowList_null_SelectedUsersRowList-Night-10_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Day-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Day-11_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Day-10_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Day-11_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Night-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Night-11_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Night-10_12_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_UnsavedAvatar_null_UnsavedAvatar-Night-11_13_null,NEXUS_5,1.0,en].png