diff --git a/changelog.d/2208.feature b/changelog.d/2208.feature new file mode 100644 index 0000000000..63dc68b585 --- /dev/null +++ b/changelog.d/2208.feature @@ -0,0 +1 @@ +Mark a room or dm as favourite. diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt index fdad01d83c..3e7fc96c68 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt @@ -20,4 +20,5 @@ sealed interface RoomDetailsEvent { data object LeaveRoom : RoomDetailsEvent data object MuteNotification : RoomDetailsEvent data object UnmuteNotification : RoomDetailsEvent + data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index a232d4e87e..2c663bfdde 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -31,6 +31,7 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -65,12 +66,13 @@ class RoomDetailsPresenter @Inject constructor( val scope = rememberCoroutineScope() val leaveRoomState = leaveRoomPresenter.present() val canShowNotificationSettings = remember { mutableStateOf(false) } - val roomInfo = room.roomInfoFlow.collectAsState(initial = null).value + val roomInfo by room.roomInfoFlow.collectAsState(initial = null) val roomAvatar by remember { derivedStateOf { roomInfo?.avatarUrl ?: room.avatarUrl } } val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.name ?: room.displayName).trim() } } val roomTopic by remember { derivedStateOf { roomInfo?.topic ?: room.topic } } + val isFavorite by remember { derivedStateOf { roomInfo?.isFavorite.orFalse() } } LaunchedEffect(Unit) { canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings) @@ -122,6 +124,11 @@ class RoomDetailsPresenter @Inject constructor( client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne) } } + is RoomDetailsEvent.SetFavorite -> { + scope.launch { + room.setIsFavorite(event.isFavorite) + } + } } } @@ -142,6 +149,7 @@ class RoomDetailsPresenter @Inject constructor( roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(), + isFavorite = isFavorite, eventSink = ::handleEvents, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index 8dc6f81bf1..5ba39af5bf 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -36,6 +36,7 @@ data class RoomDetailsState( val canShowNotificationSettings: Boolean, val leaveRoomState: LeaveRoomState, val roomNotificationSettings: RoomNotificationSettings?, + val isFavorite: Boolean, val eventSink: (RoomDetailsEvent) -> Unit ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index b84302463f..2493efc855 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -36,6 +36,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider aDmRoomDetailsState().copy(roomName = "Daniel"), aDmRoomDetailsState(isDmMemberIgnored = true).copy(roomName = "Daniel"), aRoomDetailsState().copy(canInvite = true), + aRoomDetailsState().copy(isFavorite = true), aRoomDetailsState().copy( canEdit = true, // Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed @@ -86,6 +87,7 @@ fun aRoomDetailsState() = RoomDetailsState( roomMemberDetailsState = null, leaveRoomState = aLeaveRoomState(), roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false), + isFavorite = false, eventSink = {} ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 2e7a1a22f6..efa8e6e321 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -61,6 +61,7 @@ import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.MainActionButton import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory +import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -163,6 +164,13 @@ fun RoomDetailsView( ) } + FavoriteSection( + isFavorite = state.isFavorite, + onFavoriteChanges = { + state.eventSink(RoomDetailsEvent.SetFavorite(it)) + } + ) + if (state.roomType is RoomDetailsType.Room) { MembersSection( memberCount = state.memberCount, @@ -356,6 +364,22 @@ private fun NotificationSection( } } +@Composable +private fun FavoriteSection( + isFavorite: Boolean, + onFavoriteChanges: (Boolean) -> Unit, + modifier: Modifier = Modifier +) { + PreferenceCategory(modifier = modifier) { + PreferenceSwitch( + icon = CompoundIcons.Favourite(), + title = stringResource(id = CommonStrings.common_favourite), + isChecked = isFavorite, + onCheckedChange = onFavoriteChanges + ) + } +} + @Composable private fun MembersSection( memberCount: Long, 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 299a8120a5..1faccce70e 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 @@ -19,6 +19,7 @@ package io.element.android.features.roomdetails import androidx.lifecycle.Lifecycle import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow +import app.cash.turbine.TurbineTestContext import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.test.FakeStartDMAction @@ -27,6 +28,7 @@ import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter import io.element.android.features.roomdetails.impl.RoomDetailsEvent import io.element.android.features.roomdetails.impl.RoomDetailsPresenter +import io.element.android.features.roomdetails.impl.RoomDetailsState import io.element.android.features.roomdetails.impl.RoomDetailsType import io.element.android.features.roomdetails.impl.RoomTopicState import io.element.android.features.roomdetails.impl.members.aRoomMember @@ -71,10 +73,10 @@ class RoomDetailsPresenterTests { } private fun TestScope.createRoomDetailsPresenter( - room: MatrixRoom, + room: MatrixRoom = aMatrixRoom(), leaveRoomPresenter: LeaveRoomPresenter = FakeLeaveRoomPresenter(), dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), - notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService() + notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), ): RoomDetailsPresenter { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { @@ -86,25 +88,29 @@ class RoomDetailsPresenterTests { mapOf(FeatureFlags.NotificationSettings.key to true) ) return RoomDetailsPresenter( - matrixClient, - room, - featureFlagService, - matrixClient.notificationSettingsService(), - roomMemberDetailsPresenterFactory, - leaveRoomPresenter, - dispatchers + client = matrixClient, + room = room, + featureFlagService = featureFlagService, + notificationSettingsService = matrixClient.notificationSettingsService(), + roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory, + leaveRoomPresenter = leaveRoomPresenter, + dispatchers = dispatchers, ) } + private suspend fun RoomDetailsPresenter.test(validate: suspend TurbineTestContext.() -> Unit) { + moleculeFlow(RecompositionMode.Immediate) { + withFakeLifecycleOwner(fakeLifecycleOwner) { + present() + } + }.test(validate = validate) + } + @Test fun `present - initial state is created from room if roomInfo is null`() = runTest { val room = aMatrixRoom() val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.roomId).isEqualTo(room.roomId.value) assertThat(initialState.roomName).isEqualTo(room.name) @@ -124,11 +130,7 @@ class RoomDetailsPresenterTests { givenRoomInfo(roomInfo) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { skipItems(1) val updatedState = awaitItem() assertThat(updatedState.roomName).isEqualTo(roomInfo.name) @@ -143,11 +145,7 @@ class RoomDetailsPresenterTests { fun `present - initial state with no room name`() = runTest { val room = aMatrixRoom(name = null) val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.roomName).isEqualTo(room.displayName) @@ -167,11 +165,7 @@ class RoomDetailsPresenterTests { givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember)) @@ -185,11 +179,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(true)) } val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Initially false assertThat(awaitItem().canInvite).isFalse() // Then the asynchronous check completes and it becomes true @@ -205,11 +195,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { assertThat(awaitItem().canInvite).isFalse() cancelAndIgnoreRemainingEvents() @@ -222,11 +208,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.failure(Throwable("Whoops"))) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { assertThat(awaitItem().canInvite).isFalse() cancelAndIgnoreRemainingEvents() @@ -242,11 +224,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes and it becomes true @@ -273,11 +251,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes, but editing is still disallowed because it's a DM @@ -305,11 +279,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { skipItems(1) // There's no topic, so we hide the entire UI for DMs @@ -328,11 +298,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes and it becomes true @@ -351,11 +317,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Initially false, and no further events assertThat(awaitItem().canEdit).isFalse() @@ -371,11 +333,7 @@ class RoomDetailsPresenterTests { } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // The initial state is "hidden" and no further state changes happen assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.Hidden) @@ -392,11 +350,7 @@ class RoomDetailsPresenterTests { } val presenter = createRoomDetailsPresenter(room) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { // Ignore the initial state skipItems(1) @@ -416,11 +370,7 @@ class RoomDetailsPresenterTests { leaveRoomPresenter = leaveRoomPresenter, dispatchers = testCoroutineDispatchers() ) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { awaitItem().eventSink(RoomDetailsEvent.LeaveRoom) assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId)) @@ -439,11 +389,7 @@ class RoomDetailsPresenterTests { leaveRoomPresenter = leaveRoomPresenter, notificationSettingsService = notificationSettingsService, ) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val updatedState = consumeItemsUntilPredicate { it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY @@ -458,11 +404,7 @@ class RoomDetailsPresenterTests { val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { awaitItem().eventSink(RoomDetailsEvent.MuteNotification) val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) { it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE @@ -480,11 +422,7 @@ class RoomDetailsPresenterTests { ) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - presenter.present() - } - }.test { + presenter.test { awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification) val updatedState = consumeItemsUntilPredicate { it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES @@ -493,6 +431,37 @@ class RoomDetailsPresenterTests { cancelAndIgnoreRemainingEvents() } } + + @Test + fun `present - when set is favorite event is emitted, then the action is called`() = runTest { + val room = FakeMatrixRoom() + val presenter = createRoomDetailsPresenter(room = room) + presenter.test { + val initialState = awaitItem() + initialState.eventSink(RoomDetailsEvent.SetFavorite(true)) + assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true)) + initialState.eventSink(RoomDetailsEvent.SetFavorite(false)) + assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - changes in room info updates the is favorite flag`() = runTest { + val room = aMatrixRoom() + val presenter = createRoomDetailsPresenter(room = room) + presenter.test { + room.givenRoomInfo(aRoomInfo(isFavorite = true)) + consumeItemsUntilPredicate { it.isFavorite }.last().let { state -> + assertThat(state.isFavorite).isTrue() + } + room.givenRoomInfo(aRoomInfo(isFavorite = false)) + consumeItemsUntilPredicate { !it.isFavorite }.last().let { state -> + assertThat(state.isFavorite).isFalse() + } + cancelAndIgnoreRemainingEvents() + } + } } fun aMatrixRoom( diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 10542a7f56..1ab51a5219 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -41,7 +41,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RoomListContextMenu( contextMenu: RoomListState.ContextMenu.Shown, - eventSink: (RoomListEvents.RoomListBottomSheetEvents) -> Unit, + eventSink: (RoomListEvents.ContextMenuEvents) -> Unit, onRoomSettingsClicked: (roomId: RoomId) -> Unit, ) { ModalBottomSheet( @@ -64,7 +64,10 @@ fun RoomListContextMenu( onLeaveRoomClicked = { eventSink(RoomListEvents.HideContextMenu) eventSink(RoomListEvents.LeaveRoom(contextMenu.roomId)) - } + }, + onFavoriteChanged = { isFavorite -> + eventSink(RoomListEvents.SetRoomIsFavorite(contextMenu.roomId, isFavorite)) + }, ) } } @@ -72,10 +75,11 @@ fun RoomListContextMenu( @Composable private fun RoomListModalBottomSheetContent( contextMenu: RoomListState.ContextMenu.Shown, - onRoomMarkReadClicked: () -> Unit, - onRoomMarkUnreadClicked: () -> Unit, onRoomSettingsClicked: () -> Unit, onLeaveRoomClicked: () -> Unit, + onFavoriteChanged: (isFavorite: Boolean) -> Unit, + onRoomMarkReadClicked: () -> Unit, + onRoomMarkUnreadClicked: () -> Unit, ) { Column( modifier = Modifier.fillMaxWidth() @@ -120,6 +124,30 @@ private fun RoomListModalBottomSheetContent( style = ListItemStyle.Primary, ) } + ListItem( + headlineContent = { + Text( + text = stringResource(id = CommonStrings.common_favourite), + style = MaterialTheme.typography.bodyLarge, + ) + }, + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Vector( + CompoundIcons.Favourite(), + contentDescription = stringResource(id = CommonStrings.common_favourite), + ) + ), + trailingContent = ListItemContent.Switch( + checked = contextMenu.isFavorite, + onChange = { isFavorite -> + onFavoriteChanged(isFavorite) + }, + ), + onClick = { + onFavoriteChanged(!contextMenu.isFavorite) + }, + style = ListItemStyle.Primary, + ) ListItem( headlineContent = { Text( @@ -170,7 +198,8 @@ internal fun RoomListModalBottomSheetContentPreview() = ElementPreview { onRoomMarkReadClicked = {}, onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, - onLeaveRoomClicked = {} + onLeaveRoomClicked = {}, + onFavoriteChanged = {}, ) } @@ -182,6 +211,7 @@ internal fun RoomListModalBottomSheetContentForDmPreview() = ElementPreview { onRoomMarkReadClicked = {}, onRoomMarkUnreadClicked = {}, onRoomSettingsClicked = {}, - onLeaveRoomClicked = {} + onLeaveRoomClicked = {}, + onFavoriteChanged = {}, ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt index affd3946f2..c808ae689b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt @@ -27,9 +27,10 @@ sealed interface RoomListEvents { data object ToggleSearchResults : RoomListEvents data class ShowContextMenu(val roomListRoomSummary: RoomListRoomSummary) : RoomListEvents - sealed interface RoomListBottomSheetEvents : RoomListEvents - data object HideContextMenu : RoomListBottomSheetEvents - data class LeaveRoom(val roomId: RoomId) : RoomListBottomSheetEvents - data class MarkAsRead(val roomId: RoomId) : RoomListBottomSheetEvents - data class MarkAsUnread(val roomId: RoomId) : RoomListBottomSheetEvents + sealed interface ContextMenuEvents : RoomListEvents + data object HideContextMenu : ContextMenuEvents + data class LeaveRoom(val roomId: RoomId) : ContextMenuEvents + data class MarkAsRead(val roomId: RoomId) : ContextMenuEvents + data class MarkAsUnread(val roomId: RoomId) : ContextMenuEvents + data class SetRoomIsFavorite(val roomId: RoomId, val isFavorite: Boolean) : ContextMenuEvents } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index e14a2b9aaa..68ef53703a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -51,7 +52,14 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import javax.inject.Inject @@ -111,15 +119,11 @@ class RoomListPresenter @Inject constructor( } } - val markAsUnreadFeatureFlagEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.MarkAsUnread) - .collectAsState(initial = null) - // Avatar indicator val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator() var displaySearchResults by rememberSaveable { mutableStateOf(false) } - - var contextMenu by remember { mutableStateOf(RoomListState.ContextMenu.Hidden) } + val contextMenu = remember { mutableStateOf(RoomListState.ContextMenu.Hidden) } fun handleEvents(event: RoomListEvents) { when (event) { @@ -134,16 +138,18 @@ class RoomListPresenter @Inject constructor( displaySearchResults = !displaySearchResults } is RoomListEvents.ShowContextMenu -> { - contextMenu = RoomListState.ContextMenu.Shown( - roomId = event.roomListRoomSummary.roomId, - roomName = event.roomListRoomSummary.name, - isDm = event.roomListRoomSummary.isDm, - markAsUnreadFeatureFlagEnabled = markAsUnreadFeatureFlagEnabled == true, - hasNewContent = event.roomListRoomSummary.hasNewContent - ) + coroutineScope.showContextMenu(event, contextMenu) + } + is RoomListEvents.HideContextMenu -> { + contextMenu.value = RoomListState.ContextMenu.Hidden } - is RoomListEvents.HideContextMenu -> contextMenu = RoomListState.ContextMenu.Hidden is RoomListEvents.LeaveRoom -> leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(event.roomId)) + + is RoomListEvents.SetRoomIsFavorite -> coroutineScope.launch { + client.getRoom(event.roomId)?.use { room -> + room.setIsFavorite(event.isFavorite) + } + } is RoomListEvents.MarkAsRead -> coroutineScope.launch { client.getRoom(event.roomId)?.use { room -> room.setUnreadFlag(isUnread = false) @@ -177,7 +183,7 @@ class RoomListPresenter @Inject constructor( hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, invitesState = inviteStateDataSource.inviteState(), displaySearchResults = displaySearchResults, - contextMenu = contextMenu, + contextMenu = contextMenu.value, leaveRoomState = leaveRoomState, displayMigrationStatus = isMigrating, eventSink = ::handleEvents @@ -188,6 +194,37 @@ class RoomListPresenter @Inject constructor( matrixUser.value = client.getCurrentUser() } + @OptIn(ExperimentalCoroutinesApi::class) + private fun CoroutineScope.showContextMenu(event: RoomListEvents.ShowContextMenu, contextMenuState: MutableState) = launch { + val initialState = RoomListState.ContextMenu.Shown( + roomId = event.roomListRoomSummary.roomId, + roomName = event.roomListRoomSummary.name, + isDm = event.roomListRoomSummary.isDm, + isFavorite = event.roomListRoomSummary.isFavorite, + markAsUnreadFeatureFlagEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.MarkAsUnread), + hasNewContent = event.roomListRoomSummary.hasNewContent + ) + contextMenuState.value = initialState + + client.getRoom(event.roomListRoomSummary.roomId)?.use { room -> + + val isShowingContextMenuFlow = snapshotFlow { contextMenuState.value is RoomListState.ContextMenu.Shown } + .distinctUntilChanged() + + val isFavoriteFlow = room.roomInfoFlow + .map { it.isFavorite } + .distinctUntilChanged() + + isFavoriteFlow + .onEach { isFavorite -> + contextMenuState.value = initialState.copy(isFavorite = isFavorite) + } + .flatMapLatest { isShowingContextMenuFlow } + .takeWhile { isShowingContextMenu -> isShowingContextMenu } + .collect() + } + } + private fun updateVisibleRange(range: IntRange) { if (range.isEmpty()) return val midExtendedRangeSize = EXTENDED_RANGE_SIZE / 2 diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index d2f1a55671..e97ab7c073 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -49,6 +49,7 @@ data class RoomListState( val roomId: RoomId, val roomName: String, val isDm: Boolean, + val isFavorite: Boolean, val markAsUnreadFeatureFlagEnabled: Boolean, val hasNewContent: Boolean, ) : ContextMenu diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index c975ee069a..c54360d3d3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -44,6 +44,7 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState().copy(displaySearchResults = true, filter = "", filteredRoomList = persistentListOf()), aRoomListState().copy(displaySearchResults = true), aRoomListState().copy(contextMenu = aContextMenuShown(roomName = "A nice room name")), + aRoomListState().copy(contextMenu = aContextMenuShown(isFavorite = true)), aRoomListState().copy(displayRecoveryKeyPrompt = true), aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), @@ -102,10 +103,12 @@ internal fun aContextMenuShown( roomName: String = "aRoom", isDm: Boolean = false, hasNewContent: Boolean = false, + isFavorite: Boolean = false, ) = RoomListState.ContextMenu.Shown( roomId = RoomId("!aRoom:aDomain"), roomName = roomName, isDm = isDm, markAsUnreadFeatureFlagEnabled = true, hasNewContent = hasNewContent, + isFavorite = isFavorite, ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt index d3c7c43148..02f87de745 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt @@ -49,6 +49,7 @@ class RoomListRoomSummaryFactory @Inject constructor( userDefinedNotificationMode = null, hasRoomCall = false, isDm = false, + isFavorite = false, ) } @@ -84,6 +85,7 @@ class RoomListRoomSummaryFactory @Inject constructor( userDefinedNotificationMode = roomSummary.details.userDefinedNotificationMode, hasRoomCall = roomSummary.details.hasRoomCall, isDm = roomSummary.details.isDm, + isFavorite = roomSummary.details.isFavorite, ) } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 2a5289c9db..cc9f94aa52 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -37,6 +37,7 @@ data class RoomListRoomSummary( val userDefinedNotificationMode: RoomNotificationMode?, val hasRoomCall: Boolean, val isDm: Boolean, + val isFavorite: Boolean, ) { val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE && (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) || diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index 9b80edecb0..feec962d85 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -97,6 +97,7 @@ internal fun aRoomListRoomSummary( hasRoomCall: Boolean = false, avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem), isDm: Boolean = false, + isFavorite: Boolean = false, ) = RoomListRoomSummary( id = id, roomId = RoomId(id), @@ -112,4 +113,5 @@ internal fun aRoomListRoomSummary( userDefinedNotificationMode = notificationMode, hasRoomCall = hasRoomCall, isDm = isDm, + isFavorite = isFavorite, ) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt index 6030ea1c6f..a1c98c6874 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenuTest.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureCalledOnceWithParam import io.element.android.tests.testutils.EnsureNeverCalledWithParam @@ -128,4 +129,24 @@ class RoomListContextMenuTest { eventsRecorder.assertSingle(RoomListEvents.HideContextMenu) callback.assertSuccess() } + + @Test + fun `clicking on Favourites generates expected Event`() { + val eventsRecorder = EventsRecorder() + val contextMenu = aContextMenuShown(isDm = false, isFavorite = false) + val callback = EnsureNeverCalledWithParam() + rule.setContent { + RoomListContextMenu( + contextMenu = contextMenu, + eventSink = eventsRecorder, + onRoomSettingsClicked = callback, + ) + } + rule.clickOn(CommonStrings.common_favourite) + eventsRecorder.assertList( + listOf( + RoomListEvents.SetRoomIsFavorite(contextMenu.roomId, true), + ) + ) + } } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 6ff8df00f0..f106b72fb9 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -62,6 +62,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService 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 import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService @@ -340,26 +341,49 @@ class RoomListPresenterTests { @Test fun `present - show context menu`() = runTest { val scope = CoroutineScope(coroutineContext + SupervisorJob()) - val presenter = createRoomListPresenter(coroutineScope = scope) + val room = FakeMatrixRoom() + val client = FakeMatrixClient().apply { + givenGetRoomResult(A_ROOM_ID, room) + } + val presenter = createRoomListPresenter(client = client, coroutineScope = scope) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) - val initialState = awaitItem() val summary = createRoomListRoomSummary() initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) - val shownState = awaitItem() - assertThat(shownState.contextMenu).isEqualTo( - RoomListState.ContextMenu.Shown( - roomId = summary.roomId, - roomName = summary.name, - isDm = false, - markAsUnreadFeatureFlagEnabled = true, - hasNewContent = false, - ) + awaitItem().also { state -> + assertThat(state.contextMenu) + .isEqualTo( + RoomListState.ContextMenu.Shown( + roomId = summary.roomId, + roomName = summary.name, + isDm = false, + isFavorite = false, + markAsUnreadFeatureFlagEnabled = true, + hasNewContent = false, + ) + ) + } + + room.givenRoomInfo( + aRoomInfo(isFavorite = true) ) + awaitItem().also { state -> + assertThat(state.contextMenu) + .isEqualTo( + RoomListState.ContextMenu.Shown( + roomId = summary.roomId, + roomName = summary.name, + isDm = false, + isFavorite = true, + markAsUnreadFeatureFlagEnabled = true, + hasNewContent = false, + ) + ) + } scope.cancel() } } @@ -367,7 +391,11 @@ class RoomListPresenterTests { @Test fun `present - hide context menu`() = runTest { val scope = CoroutineScope(coroutineContext + SupervisorJob()) - val presenter = createRoomListPresenter(coroutineScope = scope) + val room = FakeMatrixRoom() + val client = FakeMatrixClient().apply { + givenGetRoomResult(A_ROOM_ID, room) + } + val presenter = createRoomListPresenter(client = client, coroutineScope = scope) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -378,15 +406,18 @@ class RoomListPresenterTests { initialState.eventSink(RoomListEvents.ShowContextMenu(summary)) val shownState = awaitItem() - assertThat(shownState.contextMenu).isEqualTo( - RoomListState.ContextMenu.Shown( - roomId = summary.roomId, - roomName = summary.name, - isDm = false, - markAsUnreadFeatureFlagEnabled = true, - hasNewContent = false, + assertThat(shownState.contextMenu) + .isEqualTo( + RoomListState.ContextMenu.Shown( + roomId = summary.roomId, + roomName = summary.name, + isDm = false, + isFavorite = false, + markAsUnreadFeatureFlagEnabled = true, + hasNewContent = false, + ) ) - ) + shownState.eventSink(RoomListEvents.HideContextMenu) val hiddenState = awaitItem() @@ -440,6 +471,26 @@ class RoomListPresenterTests { } @Test + fun `present - when set is favorite event is emitted, then the action is called`() = runTest { + val scope = CoroutineScope(coroutineContext + SupervisorJob()) + val room = FakeMatrixRoom() + val client = FakeMatrixClient().apply { + givenGetRoomResult(A_ROOM_ID, room) + } + val presenter = createRoomListPresenter(client = client, coroutineScope = scope) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true)) + assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true)) + initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, false)) + assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false)) + cancelAndIgnoreRemainingEvents() + scope.cancel() + } + } + fun `present - change in migration presenter state modifies isMigrating`() = runTest { val client = FakeMatrixClient(sessionId = A_SESSION_ID) val migrationStore = InMemoryMigrationScreenStore() diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt index f36299b1b7..5cdd8ef39c 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -80,6 +80,7 @@ internal fun createRoomListRoomSummary( numberOfUnreadNotifications: Int = 0, isMarkedUnread: Boolean = false, userDefinedNotificationMode: RoomNotificationMode? = null, + isFavorite: Boolean = false, ) = RoomListRoomSummary( id = A_ROOM_ID.value, roomId = A_ROOM_ID, @@ -95,4 +96,5 @@ internal fun createRoomListRoomSummary( userDefinedNotificationMode = userDefinedNotificationMode, hasRoomCall = false, isDm = false, + isFavorite = isFavorite, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 22d8f56415..9a25938ee8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -152,6 +152,8 @@ interface MatrixRoom : Closeable { suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result + suspend fun setIsFavorite(isFavorite: Boolean): Result + /** * Mark the room as read by trying to attach an unthreaded read receipt to the latest room event. * @param receiptType The type of receipt to send. 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 1304ca40a8..c35154798b 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 @@ -30,6 +30,7 @@ data class MatrixRoomInfo( val isPublic: Boolean, val isSpace: Boolean, val isTombstoned: Boolean, + val isFavorite: Boolean, val canonicalAlias: String?, val alternativeAliases: ImmutableList, val currentUserMembership: CurrentUserMembership, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index b8e6dbd84f..07fa9154bd 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -48,6 +48,7 @@ data class RoomSummaryDetails( val userDefinedNotificationMode: RoomNotificationMode?, val hasRoomCall: Boolean, val isDm: Boolean, + val isFavorite: Boolean, ) { val lastMessageTimestamp = lastMessage?.originServerTs } 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 909649441f..eea471b696 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 @@ -40,6 +40,7 @@ class MatrixRoomInfoMapper( isPublic = it.isPublic, isSpace = it.isSpace, isTombstoned = it.isTombstoned, + isFavorite = it.isFavourite, canonicalAlias = it.canonicalAlias, alternativeAliases = it.alternativeAliases.toImmutableList(), currentUserMembership = it.membership.map(), diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 3ce35cfaa6..6e1b06ac51 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -442,6 +442,12 @@ class RustMatrixRoom( } } + override suspend fun setIsFavorite(isFavorite: Boolean): Result = withContext(roomDispatcher) { + runCatching { + innerRoom.setIsFavourite(isFavorite, null) + } + } + override suspend fun markAsRead(receiptType: ReceiptType): Result = withContext(roomDispatcher) { runCatching { innerRoom.markAsRead(receiptType.toRustReceiptType()) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt index cd3c86871b..e740c36605 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt @@ -44,6 +44,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode), hasRoomCall = roomInfo.hasRoomCall, isDm = roomInfo.isDirect && roomInfo.activeMembersCount.toLong() == 2L, + isFavorite = roomInfo.isFavourite, ) } } 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 b3bfa66328..4061a9dd9c 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 @@ -114,6 +114,7 @@ class FakeMatrixRoom( private var getWidgetDriverResult: Result = Result.success(FakeWidgetDriver()) private var canUserTriggerRoomNotificationResult: Result = Result.success(true) private var canUserJoinCallResult: Result = Result.success(true) + private var setIsFavoriteResult = Result.success(Unit) var sendMessageMentions = emptyList() val editMessageCalls = mutableListOf>() private val _typingRecord = mutableListOf() @@ -378,6 +379,14 @@ class FakeMatrixRoom( return reportContentResult } + val setIsFavoriteCalls = mutableListOf() + + override suspend fun setIsFavorite(isFavorite: Boolean): Result { + return setIsFavoriteResult.also { + setIsFavoriteCalls.add(isFavorite) + } + } + val markAsReadCalls = mutableListOf() override suspend fun markAsRead(receiptType: ReceiptType): Result { @@ -590,6 +599,10 @@ class FakeMatrixRoom( getWidgetDriverResult = result } + fun givenSetIsFavoriteResult(result: Result) { + setIsFavoriteResult = result + } + fun givenRoomInfo(roomInfo: MatrixRoomInfo) { _roomInfoFlow.tryEmit(roomInfo) } @@ -633,6 +646,7 @@ fun aRoomInfo( isPublic: Boolean = true, isSpace: Boolean = false, isTombstoned: Boolean = false, + isFavorite: Boolean = false, canonicalAlias: String? = null, alternativeAliases: List = emptyList(), currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED, @@ -655,6 +669,7 @@ fun aRoomInfo( isPublic = isPublic, isSpace = isSpace, isTombstoned = isTombstoned, + isFavorite = isFavorite, canonicalAlias = canonicalAlias, alternativeAliases = alternativeAliases.toImmutableList(), currentUserMembership = currentUserMembership, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 4871dcb402..a1ef41b742 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -68,6 +68,7 @@ fun aRoomSummaryDetails( canonicalAlias: String? = null, hasRoomCall: Boolean = false, isDm: Boolean = false, + isFavorite: Boolean = false, ) = RoomSummaryDetails( roomId = roomId, name = name, @@ -83,6 +84,7 @@ fun aRoomSummaryDetails( canonicalAlias = canonicalAlias, hasRoomCall = hasRoomCall, isDm = isDm, + isFavorite = isFavorite, ) fun aRoomMessage( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index 47739817e1..3015c81355 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -117,6 +117,7 @@ fun aRoomSummaryDetails( numUnreadMessages: Int = 0, numUnreadNotifications: Int = 0, isMarkedUnread: Boolean = false, + isFavorite: Boolean = false, ) = RoomSummaryDetails( roomId = roomId, name = name, @@ -132,4 +133,5 @@ fun aRoomSummaryDetails( numUnreadMessages = numUnreadMessages, numUnreadNotifications = numUnreadNotifications, isMarkedUnread = isMarkedUnread, + isFavorite = isFavorite, ) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png index 2d80565669..2e0d2f52f4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2dd92e3cf76367608276fd10030d161ff370294b474c3e08fbf6463508fb1b0 -size 42064 +oid sha256:10c67ba7b16d81e030cb44719cc68a0a3fd57acba75e488a37c3bdf4f739cf95 +size 44537 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png index dc6ad12b0d..0016e78cb8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb09fb2b24e1597e07898562b29cee3e62ad63040ab77895c234a13f4836ab08 -size 30442 +oid sha256:a17d073bf7c3e993c81e51e234c4f885d2264f6de8bf54a93306f14ee5a02a66 +size 33354 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png index 3ff5018aec..c86faebfbc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e482480e6103d81fa6e08857c3308bde87255ec2bed55ea616eca1cf3941cc -size 32698 +oid sha256:ea52227dcdf0806e292971e1b7332db28964eadd242882a80b6486d691e9c2c9 +size 35219 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png index f22a3b63e1..a130ba4351 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63e7bf82b003a869d6ccfbc5057ddfe0f447bd7eb31801df828c410744d15223 -size 32008 +oid sha256:0163f939dfc3103eff7ded67b2fbd31dd050f6a4150245c9f0fbf3c7d13daf3e +size 34806 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png index be699c15b7..5b02de2ffb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00ca19bd4146e4b7937627a9aadd900cbf928e809996d7e1c950c0ed03aa888f -size 39635 +oid sha256:ab7c7c6ae6951bf173f24fddb70e99feacd92286b088a6921900c6b7e881000a +size 42122 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png index a5fc0f06cd..0d6bc21206 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ea77bd41442b6686fcbe53a4ea1bb29a6040a78ceb8f6e5453fddc0b4ae6b35 -size 39892 +oid sha256:4063c0c9f763304aea54a73aaf95efc636d029cab45369b24a3bfc0b3bf3731d +size 42774 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png index a5fc0f06cd..0d6bc21206 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ea77bd41442b6686fcbe53a4ea1bb29a6040a78ceb8f6e5453fddc0b4ae6b35 -size 39892 +oid sha256:4063c0c9f763304aea54a73aaf95efc636d029cab45369b24a3bfc0b3bf3731d +size 42774 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png index 622259fef7..9f0e81e3c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:beeb97ce692ae6592dd61769cfdf87f3fc3518548b02443e9ef6134e3dec01bb -size 43479 +oid sha256:dabfea9a1b7604977444e8fedc977a61ab5576c6115aa97bffd77ad1d5c65038 +size 46198 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png index 00bab0f541..351feab0c8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a5ef5d83bc9dd2e3bef092d6620baaf0f0f94657f41961bb1cf2b547ae10415 -size 41931 +oid sha256:d1dfdb2a8cca036992e06a4d2a626c32d40c899e85bd5d5cf3aaba4272d339d8 +size 44244 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..66ac910cef --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_1_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd7974c00bce66a8a1715e956c64aa578db303ff287aefb9dc5bb7906e4e27d5 +size 44406 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png index 6ab436dec6..947b00cb3d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a6e83b3b6880ee6eec8731e42e744c0fe6f806cbad6c1925cb297e19c1b811b -size 43222 +oid sha256:258605b56cbfb5ade4ce390b7e38a37b0602612d45a751b0032e4ba757d8d76f +size 45766 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png index 3bdb76b2d7..5d76493733 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3d669d780e1b7a3c741d5f6c8f20e8ffe80b283291f133672221cddcca192dc -size 31530 +oid sha256:c984e6333a0db11f1e52f246f019b927d97910dd35f5b79aae6d7c4f3094f913 +size 34667 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png index 5a19953067..a640c79a22 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee48e602a928b83bb547efe6352c419d1bf71ea8490226884f715f3266d493e5 -size 34080 +oid sha256:613b0bd1053c46b45fe6b58c754b03c69bab0a6cc2d90cd99278b4786f1253b0 +size 36681 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png index ab41b3c954..400b589f07 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e8f719df1249e31f26022815930577f9baf135454ff260664fa65045c1b690 -size 32687 +oid sha256:97416e171d87d7290924270c175b44a10c2b25fd3541156bf9e5258975f0c3a4 +size 35627 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png index 5a2f5937f0..14f86e1167 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee5dde97c04f0ccdc21349158150e11ad1a1b91c97bfd3aaefacc4e0e1a17a8a -size 40828 +oid sha256:28a271552ea0e61954646c47bd8e87048c92cace7ca4176901c7b4d000bbc53f +size 43375 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png index 148b6503b7..c4fddf0f15 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfd34cc11af2c77b25e19d4493db998d8cad0efed379dce3a9537b2c49fda28 -size 41039 +oid sha256:7a4ddad1a6f2afad344e8e88fd297c91a5064206b2dbcb1130dd15b5fe22fbc9 +size 44086 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png index 148b6503b7..c4fddf0f15 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfd34cc11af2c77b25e19d4493db998d8cad0efed379dce3a9537b2c49fda28 -size 41039 +oid sha256:7a4ddad1a6f2afad344e8e88fd297c91a5064206b2dbcb1130dd15b5fe22fbc9 +size 44086 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png index c329d038db..49af7727ee 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:567e1b09ae9383c8af71c7d66d2877091c0ce3d98eab1d92c7bc9d2f896875bc -size 44629 +oid sha256:36e579b569043783e8890a6cf5ffa1ae511c524c20f923765825ab9a9ccf9bb4 +size 47592 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png index 253475f636..3571ab69cd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb8863bcb78be69e715ec9bbf3ef7ee51fd142b484a52a22b381373708d5049e -size 43101 +oid sha256:2b27ecb48f63242353d750c5fb2eb5cf2fcb3c11edd046cc9c03db57f3d26122 +size 45472 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..693457317c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_0_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fb2205cfbb68ae2e808fe62765311b10921a8bd579e23803484a492de36d028 +size 45645 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png index 4eb32e5121..a6140d1dba 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Day-2_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d636ef74890668b0d2cc49af71b1e3734237fa1f9944afb89d8152cb9a839cd4 -size 17391 +oid sha256:404fc390042c6d7870e2fbef176eb497ac3fb474c53483c085cd04038d0192ef +size 21918 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png index 676aa25413..698bd9c32b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContentForDm_null_RoomListModalBottomSheetContentForDm-Night-2_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c883b6e8c7ae3570f2d7370a929cc9afd2a1983bc6064f50090ad8e8c9295d4 -size 16289 +oid sha256:4791d9b6fa50a29c10b618d27786033b69983229a9ce2e10b83e35cbe4a6365a +size 20671 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png index 115580282c..9ecd0d5c68 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6b6fe672b31db43d1aeed7d0835d638b729c09ac01b03fcdfd814b2e1d861d5 -size 15553 +oid sha256:d3b0fe4d5ca9f4b735ed43ea167c238d2086a2aa6f8760fe74841bc6f0a1be6c +size 20289 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png index f4410234ee..87fa39e373 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:178e6f2f9aa771831eb3f672cf212d5b3a37532c02dcc5126446965b49be7bdd -size 14558 +oid sha256:1d80d0580828e0f657ff2f36915c2d0a7a9d56612007da87c3b9676f882131bc +size 19023 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png index cdfb465771..4e360c7666 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2523a28a889fd1739fe1e0c94edce5d86a5ebcbe6c3973a3c49fbb8fb8a19f79 -size 55617 +oid sha256:5f8802121f779f48dd556a9a3a08e6ac1a63e86d5695f6f8c133fbb346d76ef6 +size 89779 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png index 06846f3231..cdfb465771 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13dadbd502163a9bfe81cb1e67f9aa0933c4b7f4fd3c8f731e930a28709485d2 -size 51948 +oid sha256:2523a28a889fd1739fe1e0c94edce5d86a5ebcbe6c3973a3c49fbb8fb8a19f79 +size 55617 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png index f901e915e5..06846f3231 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64c4eb481f40871925405ae317cb80927caf31cb552a8aa9549bfb5658ca91e4 -size 137589 +oid sha256:13dadbd502163a9bfe81cb1e67f9aa0933c4b7f4fd3c8f731e930a28709485d2 +size 51948 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f901e915e5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64c4eb481f40871925405ae317cb80927caf31cb552a8aa9549bfb5658ca91e4 +size 137589 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png index 4e360c7666..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f8802121f779f48dd556a9a3a08e6ac1a63e86d5695f6f8c133fbb346d76ef6 -size 89779 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png index a7fd32602e..a751592e9b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b25e023153dd094299a0c794d5e488bdc6fc0c478abbfbce7337e52b233715d -size 57463 +oid sha256:7cdd93c157565f7f8d3fa23a43c30e02531dab40578a315c5d0d10a99e74f259 +size 91394 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png index 65b76802d2..a7fd32602e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a00391ef16a762a14dd7e348ce97b500ca1b42a32ee3ff926a10865f46cd06c -size 53590 +oid sha256:2b25e023153dd094299a0c794d5e488bdc6fc0c478abbfbce7337e52b233715d +size 57463 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png index 56f7f133f3..65b76802d2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7bf47b0a25c455b108d7b3585a42849f03c6652af73a175fe2147cb1ad62a66 -size 161125 +oid sha256:0a00391ef16a762a14dd7e348ce97b500ca1b42a32ee3ff926a10865f46cd06c +size 53590 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..56f7f133f3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7bf47b0a25c455b108d7b3585a42849f03c6652af73a175fe2147cb1ad62a66 +size 161125 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png index a751592e9b..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cdd93c157565f7f8d3fa23a43c30e02531dab40578a315c5d0d10a99e74f259 -size 91394 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462