diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContentStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContentStateProvider.kt new file mode 100644 index 0000000000..284abb5d0f --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContentStateProvider.kt @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package io.element.android.features.roomlist.impl + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +open class RoomListContentStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aRoomsContentState(), + aRoomsContentState(summaries = persistentListOf()), + aSkeletonContentState(), + anEmptyContentState(), + aMigrationContentState(), + ) +} + +internal fun aRoomsContentState( + invitesState: InvitesState = InvitesState.NoInvites, + securityBannerState: SecurityBannerState = SecurityBannerState.None, + summaries: ImmutableList = aRoomListRoomSummaryList(), +) = RoomListContentState.Rooms( + invitesState = invitesState, + securityBannerState = securityBannerState, + summaries = summaries, +) + +internal fun aMigrationContentState() = RoomListContentState.Migration + +internal fun aSkeletonContentState() = RoomListContentState.Skeleton(16) + +internal fun anEmptyContentState( + invitesState: InvitesState = InvitesState.NoInvites, +) = RoomListContentState.Empty(invitesState) 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 245065a52f..5197361f2d 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 @@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -26,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow @@ -38,7 +40,7 @@ import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.filters.RoomListFiltersState -import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter +import io.element.android.features.roomlist.impl.migration.MigrationScreenState import io.element.android.features.roomlist.impl.search.RoomListSearchEvents import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.libraries.architecture.AsyncData @@ -52,6 +54,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState +import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.timeline.ReceiptType @@ -60,6 +63,7 @@ import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect @@ -85,7 +89,7 @@ class RoomListPresenter @Inject constructor( private val indicatorService: IndicatorService, private val filtersPresenter: Presenter, private val searchPresenter: Presenter, - private val migrationScreenPresenter: MigrationScreenPresenter, + private val migrationScreenPresenter: Presenter, private val sessionPreferencesStore: SessionPreferencesStore, private val analyticsService: AnalyticsService, ) : Presenter { @@ -100,11 +104,7 @@ class RoomListPresenter @Inject constructor( val matrixUser: MutableState = rememberSaveable { mutableStateOf(null) } - val roomList by produceState(initialValue = AsyncData.Loading()) { - roomListDataSource.allRooms.collect { value = AsyncData.Success(it) } - } val networkConnectionStatus by networkMonitor.connectivity.collectAsState() - val filtersState = filtersPresenter.present() val searchState = searchPresenter.present() @@ -113,28 +113,7 @@ class RoomListPresenter @Inject constructor( initialLoad(matrixUser) } - val isMigrating = migrationScreenPresenter.present().isMigrating - var securityBannerDismissed by rememberSaveable { mutableStateOf(false) } - val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false) - val isLastDevice by encryptionService.isLastDevice.collectAsState() - val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() - val syncState by syncService.syncState.collectAsState() - val securityBannerState by remember { - derivedStateOf { - when { - securityBannerDismissed -> SecurityBannerState.None - canVerifySession -> if (isLastDevice) { - SecurityBannerState.RecoveryKeyConfirmation - } else { - SecurityBannerState.SessionVerification - } - recoveryState == RecoveryState.INCOMPLETE && - syncState == SyncState.Running -> SecurityBannerState.RecoveryKeyConfirmation - else -> SecurityBannerState.None - } - } - } // Avatar indicator val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator() @@ -162,19 +141,18 @@ class RoomListPresenter @Inject constructor( val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() + val contentState = roomListContentState(securityBannerDismissed) + return RoomListState( matrixUser = matrixUser.value, showAvatarIndicator = showAvatarIndicator, - roomList = roomList, - securityBannerState = securityBannerState, snackbarMessage = snackbarMessage, hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, - invitesState = inviteStateDataSource.inviteState(), contextMenu = contextMenu.value, leaveRoomState = leaveRoomState, filtersState = filtersState, searchState = searchState, - displayMigrationStatus = isMigrating, + contentState = contentState, eventSink = ::handleEvents, ) } @@ -183,6 +161,70 @@ class RoomListPresenter @Inject constructor( matrixUser.value = client.getCurrentUser() } + @Composable + private fun securityBannerState( + securityBannerDismissed: Boolean, + ): State { + val currentSecurityBannerDismissed by rememberUpdatedState(securityBannerDismissed) + val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false) + val isLastDevice by encryptionService.isLastDevice.collectAsState() + val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() + val syncState by syncService.syncState.collectAsState() + return remember { + derivedStateOf { + when { + currentSecurityBannerDismissed -> SecurityBannerState.None + canVerifySession -> if (isLastDevice) { + SecurityBannerState.RecoveryKeyConfirmation + } else { + SecurityBannerState.SessionVerification + } + recoveryState == RecoveryState.INCOMPLETE && + syncState == SyncState.Running -> SecurityBannerState.RecoveryKeyConfirmation + else -> SecurityBannerState.None + } + } + } + } + + @Composable + private fun roomListContentState( + securityBannerDismissed: Boolean, + ): RoomListContentState { + val roomSummaries by produceState(initialValue = AsyncData.Loading()) { + roomListDataSource.allRooms.collect { value = AsyncData.Success(it) } + } + val loadingState by roomListDataSource.loadingState.collectAsState() + val showMigration = migrationScreenPresenter.present().isMigrating + val showEmpty by remember { + derivedStateOf { + (loadingState as? RoomList.LoadingState.Loaded)?.numberOfRooms == 0 + } + } + val showSkeleton by remember { + derivedStateOf { + loadingState == RoomList.LoadingState.NotLoaded || roomSummaries is AsyncData.Loading + } + } + return when { + showMigration -> RoomListContentState.Migration + showEmpty -> { + val invitesState = inviteStateDataSource.inviteState() + RoomListContentState.Empty(invitesState) + } + showSkeleton -> RoomListContentState.Skeleton(count = 16) + else -> { + val invitesState = inviteStateDataSource.inviteState() + val securityBannerState by securityBannerState(securityBannerDismissed) + RoomListContentState.Rooms( + invitesState = invitesState, + securityBannerState = securityBannerState, + summaries = roomSummaries.dataOrNull().orEmpty().toPersistentList() + ) + } + } + } + @OptIn(ExperimentalCoroutinesApi::class) private fun CoroutineScope.showContextMenu(event: RoomListEvents.ShowContextMenu, contextMenuState: MutableState) = launch { val initialState = RoomListState.ContextMenu.Shown( 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 c4581732eb..73a1460ad9 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 @@ -21,7 +21,6 @@ import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.roomlist.impl.filters.RoomListFiltersState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchState -import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -31,20 +30,17 @@ import kotlinx.collections.immutable.ImmutableList data class RoomListState( val matrixUser: MatrixUser?, val showAvatarIndicator: Boolean, - val roomList: AsyncData>, - val securityBannerState: SecurityBannerState, val hasNetworkConnection: Boolean, val snackbarMessage: SnackbarMessage?, - val invitesState: InvitesState, val contextMenu: ContextMenu, val leaveRoomState: LeaveRoomState, val filtersState: RoomListFiltersState, val searchState: RoomListSearchState, - val displayMigrationStatus: Boolean, + val contentState: RoomListContentState, val eventSink: (RoomListEvents) -> Unit, ) { - val displayFilters = filtersState.isFeatureEnabled && !displayMigrationStatus - val displayEmptyState = roomList is AsyncData.Success && roomList.data.isEmpty() + val displayFilters = filtersState.isFeatureEnabled && contentState is RoomListContentState.Rooms + val displayActions = contentState !is RoomListContentState.Migration sealed interface ContextMenu { data object Hidden : ContextMenu @@ -70,3 +66,15 @@ enum class SecurityBannerState { SessionVerification, RecoveryKeyConfirmation, } + +@Immutable +sealed interface RoomListContentState { + data object Migration : RoomListContentState + data class Skeleton(val count: Int) : RoomListContentState + data class Empty(val invitesState: InvitesState) : RoomListContentState + data class Rooms( + val invitesState: InvitesState, + val securityBannerState: SecurityBannerState, + val summaries: ImmutableList, + ) : RoomListContentState +} 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 bfc155f2d2..3d577d9ea9 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 @@ -19,14 +19,12 @@ package io.element.android.features.roomlist.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState -import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.filters.RoomListFiltersState import io.element.android.features.roomlist.impl.filters.aRoomListFiltersState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.features.roomlist.impl.search.aRoomListSearchState -import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -43,15 +41,15 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState(), aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)), aRoomListState(hasNetworkConnection = false), - aRoomListState(invitesState = InvitesState.SeenInvites), - aRoomListState(invitesState = InvitesState.NewInvites), + aRoomListState(contentState = aRoomsContentState(invitesState = InvitesState.SeenInvites)), + aRoomListState(contentState = aRoomsContentState(invitesState = InvitesState.NewInvites)), aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")), aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)), - aRoomListState(securityBannerState = SecurityBannerState.SessionVerification), - aRoomListState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), - aRoomListState(roomList = AsyncData.Success(persistentListOf())), - aRoomListState(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), - aRoomListState(matrixUser = null, displayMigrationStatus = true), + aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SessionVerification)), + aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation)), + aRoomListState(contentState = anEmptyContentState()), + aRoomListState(contentState = aSkeletonContentState()), + aRoomListState(matrixUser = null, contentState = aMigrationContentState()), aRoomListState(searchState = aRoomListSearchState(isSearchActive = true, query = "Test")), aRoomListState(filtersState = aRoomListFiltersState(isFeatureEnabled = true)), ) @@ -60,30 +58,24 @@ open class RoomListStateProvider : PreviewParameterProvider { internal fun aRoomListState( matrixUser: MatrixUser? = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"), showAvatarIndicator: Boolean = false, - roomList: AsyncData> = AsyncData.Success(aRoomListRoomSummaryList()), hasNetworkConnection: Boolean = true, snackbarMessage: SnackbarMessage? = null, - securityBannerState: SecurityBannerState = SecurityBannerState.None, - invitesState: InvitesState = InvitesState.NoInvites, contextMenu: RoomListState.ContextMenu = RoomListState.ContextMenu.Hidden, leaveRoomState: LeaveRoomState = aLeaveRoomState(), searchState: RoomListSearchState = aRoomListSearchState(), filtersState: RoomListFiltersState = aRoomListFiltersState(isFeatureEnabled = false), - displayMigrationStatus: Boolean = false, + contentState: RoomListContentState = aRoomsContentState(), eventSink: (RoomListEvents) -> Unit = {} ) = RoomListState( matrixUser = matrixUser, showAvatarIndicator = showAvatarIndicator, - roomList = roomList, hasNetworkConnection = hasNetworkConnection, snackbarMessage = snackbarMessage, - securityBannerState = securityBannerState, - invitesState = invitesState, contextMenu = contextMenu, leaveRoomState = leaveRoomState, - searchState = searchState, filtersState = filtersState, - displayMigrationStatus = displayMigrationStatus, + searchState = searchState, + contentState = contentState, eventSink = eventSink, ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index ab261c0cd5..23a105f143 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -17,62 +17,36 @@ package io.element.android.features.roomlist.impl import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer -import io.element.android.features.roomlist.impl.components.ConfirmRecoveryKeyBanner -import io.element.android.features.roomlist.impl.components.RequestVerificationHeader +import io.element.android.features.roomlist.impl.components.RoomListContentView import io.element.android.features.roomlist.impl.components.RoomListMenuAction import io.element.android.features.roomlist.impl.components.RoomListTopBar -import io.element.android.features.roomlist.impl.components.RoomSummaryRow -import io.element.android.features.roomlist.impl.filters.RoomListFiltersView -import io.element.android.features.roomlist.impl.migration.MigrationScreenView import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.FloatingActionButton -import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.ui.strings.CommonStrings @Composable fun RoomListView( @@ -108,7 +82,7 @@ fun RoomListView( LeaveRoomView(state = state.leaveRoomState) - RoomListContent( + RoomListScaffold( modifier = Modifier.padding(top = topPadding), state = state, onVerifyClicked = onVerifyClicked, @@ -135,43 +109,9 @@ fun RoomListView( } } -@Composable -private fun EmptyRoomListView( - onCreateRoomClicked: () -> Unit, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text( - text = stringResource(R.string.screen_roomlist_empty_title), - style = ElementTheme.typography.fontBodyLgRegular, - color = ElementTheme.colors.textSecondary, - textAlign = TextAlign.Center, - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = stringResource(R.string.screen_roomlist_empty_message), - style = ElementTheme.typography.fontBodyLgRegular, - color = ElementTheme.colors.textSecondary, - textAlign = TextAlign.Center, - ) - Spacer(modifier = Modifier.height(16.dp)) - Button( - text = stringResource(CommonStrings.action_start_chat), - leadingIcon = IconSource.Vector(CompoundIcons.Compose()), - onClick = onCreateRoomClicked, - ) - } -} - @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun RoomListContent( +private fun RoomListScaffold( state: RoomListState, onVerifyClicked: () -> Unit, onConfirmRecoveryKeyClicked: () -> Unit, @@ -188,111 +128,43 @@ private fun RoomListContent( } val appBarState = rememberTopAppBarState() - val lazyListState = rememberLazyListState() - - val visibleRange by remember { - derivedStateOf { - val layoutInfo = lazyListState.layoutInfo - val firstItemIndex = layoutInfo.visibleItemsInfo.firstOrNull()?.index ?: 0 - val size = layoutInfo.visibleItemsInfo.size - firstItemIndex until firstItemIndex + size - } - } val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState) - val nestedScrollConnection = remember { - object : NestedScrollConnection { - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - state.eventSink(RoomListEvents.UpdateVisibleRange(visibleRange)) - return super.onPostFling(consumed, available) - } - } - } - val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) Scaffold( modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { - Column { - RoomListTopBar( - matrixUser = state.matrixUser, - showAvatarIndicator = state.showAvatarIndicator, - areSearchResultsDisplayed = state.searchState.isSearchActive, - onToggleSearch = { state.eventSink(RoomListEvents.ToggleSearchResults) }, - onMenuActionClicked = onMenuActionClicked, - onOpenSettings = onOpenSettings, - scrollBehavior = scrollBehavior, - displayMenuItems = !state.displayMigrationStatus, - ) - if (state.displayFilters) { - RoomListFiltersView(state = state.filtersState) - } - } + RoomListTopBar( + matrixUser = state.matrixUser, + showAvatarIndicator = state.showAvatarIndicator, + areSearchResultsDisplayed = state.searchState.isSearchActive, + onToggleSearch = { state.eventSink(RoomListEvents.ToggleSearchResults) }, + onMenuActionClicked = onMenuActionClicked, + onOpenSettings = onOpenSettings, + scrollBehavior = scrollBehavior, + displayMenuItems = state.displayActions, + displayFilters = state.displayFilters, + filtersState = state.filtersState, + ) }, content = { padding -> - LazyColumn( + RoomListContentView( + contentState = state.contentState, + filtersState = state.filtersState, + eventSink = state.eventSink, + onVerifyClicked = onVerifyClicked, + onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked, + onRoomClicked = ::onRoomClicked, + onRoomLongClicked = onRoomLongClicked, + onCreateRoomClicked = onCreateRoomClicked, + onInvitesClicked = onInvitesClicked, modifier = Modifier .padding(padding) .consumeWindowInsets(padding) - .nestedScroll(nestedScrollConnection), - state = lazyListState, - // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 - contentPadding = PaddingValues(bottom = 80.dp) - ) { - when { - state.displayEmptyState -> Unit - state.securityBannerState == SecurityBannerState.SessionVerification -> { - item { - RequestVerificationHeader( - onVerifyClicked = onVerifyClicked, - onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) } - ) - } - } - state.securityBannerState == SecurityBannerState.RecoveryKeyConfirmation -> { - item { - ConfirmRecoveryKeyBanner( - onContinueClicked = onConfirmRecoveryKeyClicked, - onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) } - ) - } - } - } - - if (state.invitesState != InvitesState.NoInvites) { - item { - InvitesEntryPointView(onInvitesClicked, state.invitesState) - } - } - - val roomList = state.roomList.dataOrNull().orEmpty() - // Note: do not use a key for the LazyColumn, or the scroll will not behave as expected if a room - // is moved to the top of the list. - itemsIndexed( - items = roomList, - contentType = { _, room -> room.contentType() }, - ) { index, room -> - RoomSummaryRow( - room = room, - onClick = ::onRoomClicked, - onLongClick = onRoomLongClicked, - ) - if (index != roomList.lastIndex) { - HorizontalDivider() - } - } - } - if (state.displayEmptyState) { - if (state.filtersState.hasAnyFilterSelected) { - // TODO add empty state for filtered rooms - } else { - EmptyRoomListView(onCreateRoomClicked) - } - } - MigrationScreenView(isMigrating = state.displayMigrationStatus) + ) }, floatingActionButton = { - if (!state.displayMigrationStatus) { + if (state.displayActions) { FloatingActionButton( // FIXME align on Design system theme containerColor = MaterialTheme.colorScheme.primary, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt new file mode 100644 index 0000000000..1bb5af4e7a --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt @@ -0,0 +1,325 @@ +/* + * 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.roomlist.impl.components + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.roomlist.impl.InvitesEntryPointView +import io.element.android.features.roomlist.impl.InvitesState +import io.element.android.features.roomlist.impl.R +import io.element.android.features.roomlist.impl.RoomListContentState +import io.element.android.features.roomlist.impl.RoomListContentStateProvider +import io.element.android.features.roomlist.impl.RoomListEvents +import io.element.android.features.roomlist.impl.SecurityBannerState +import io.element.android.features.roomlist.impl.contentType +import io.element.android.features.roomlist.impl.filters.RoomListFilter +import io.element.android.features.roomlist.impl.filters.RoomListFiltersEmptyStateResources +import io.element.android.features.roomlist.impl.filters.RoomListFiltersState +import io.element.android.features.roomlist.impl.filters.aRoomListFiltersState +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionState +import io.element.android.features.roomlist.impl.migration.MigrationScreenView +import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.HorizontalDivider +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun RoomListContentView( + contentState: RoomListContentState, + filtersState: RoomListFiltersState, + eventSink: (RoomListEvents) -> Unit, + onVerifyClicked: () -> Unit, + onConfirmRecoveryKeyClicked: () -> Unit, + onRoomClicked: (RoomListRoomSummary) -> Unit, + onRoomLongClicked: (RoomListRoomSummary) -> Unit, + onCreateRoomClicked: () -> Unit, + onInvitesClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier) { + when (contentState) { + is RoomListContentState.Migration -> { + MigrationScreenView(isMigrating = true) + } + is RoomListContentState.Skeleton -> { + SkeletonView( + count = contentState.count, + ) + } + is RoomListContentState.Empty -> { + EmptyView( + state = contentState, + onInvitesClicked = onInvitesClicked, + onCreateRoomClicked = onCreateRoomClicked, + ) + } + is RoomListContentState.Rooms -> { + RoomsView( + state = contentState, + filtersState = filtersState, + eventSink = eventSink, + onVerifyClicked = onVerifyClicked, + onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked, + onRoomClicked = onRoomClicked, + onRoomLongClicked = onRoomLongClicked, + onInvitesClicked = onInvitesClicked, + ) + } + } + } +} + +@Composable +private fun SkeletonView(count: Int, modifier: Modifier = Modifier) { + LazyColumn(modifier = modifier) { + repeat(count) { index -> + item { + RoomSummaryPlaceholderRow() + if (index != count - 1) { + HorizontalDivider() + } + } + } + } +} + +@Composable +private fun EmptyView( + state: RoomListContentState.Empty, + onCreateRoomClicked: () -> Unit, + onInvitesClicked: () -> Unit, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier.fillMaxSize(), + ) { + if (state.invitesState != InvitesState.NoInvites) { + InvitesEntryPointView(onInvitesClicked, state.invitesState) + } + EmptyScaffold( + title = R.string.screen_roomlist_empty_title, + subtitle = R.string.screen_roomlist_empty_message, + action = { + Button( + text = stringResource(CommonStrings.action_start_chat), + leadingIcon = IconSource.Vector(CompoundIcons.Compose()), + onClick = onCreateRoomClicked, + ) + }, + modifier = Modifier.fillMaxSize(), + ) + } +} + +@Composable +private fun RoomsView( + state: RoomListContentState.Rooms, + filtersState: RoomListFiltersState, + eventSink: (RoomListEvents) -> Unit, + onVerifyClicked: () -> Unit, + onConfirmRecoveryKeyClicked: () -> Unit, + onRoomClicked: (RoomListRoomSummary) -> Unit, + onRoomLongClicked: (RoomListRoomSummary) -> Unit, + onInvitesClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) { + EmptyViewForFilterStates( + selectedFilters = filtersState.selectedFilters(), + modifier = modifier.fillMaxSize() + ) + } else { + RoomsViewList( + state = state, + eventSink = eventSink, + onVerifyClicked = onVerifyClicked, + onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked, + onRoomClicked = onRoomClicked, + onRoomLongClicked = onRoomLongClicked, + onInvitesClicked = onInvitesClicked, + modifier = modifier.fillMaxSize(), + ) + } +} + +@Composable +private fun RoomsViewList( + state: RoomListContentState.Rooms, + eventSink: (RoomListEvents) -> Unit, + onVerifyClicked: () -> Unit, + onConfirmRecoveryKeyClicked: () -> Unit, + onRoomClicked: (RoomListRoomSummary) -> Unit, + onRoomLongClicked: (RoomListRoomSummary) -> Unit, + onInvitesClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + val lazyListState = rememberLazyListState() + val visibleRange by remember { + derivedStateOf { + val layoutInfo = lazyListState.layoutInfo + val firstItemIndex = layoutInfo.visibleItemsInfo.firstOrNull()?.index ?: 0 + val size = layoutInfo.visibleItemsInfo.size + firstItemIndex until firstItemIndex + size + } + } + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + eventSink(RoomListEvents.UpdateVisibleRange(visibleRange)) + return super.onPostFling(consumed, available) + } + } + } + LazyColumn( + state = lazyListState, + modifier = modifier.nestedScroll(nestedScrollConnection), + // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 + contentPadding = PaddingValues(bottom = 80.dp) + ) { + when (state.securityBannerState) { + SecurityBannerState.SessionVerification -> { + item { + RequestVerificationHeader( + onVerifyClicked = onVerifyClicked, + onDismissClicked = { eventSink(RoomListEvents.DismissRequestVerificationPrompt) } + ) + } + } + SecurityBannerState.RecoveryKeyConfirmation -> { + item { + ConfirmRecoveryKeyBanner( + onContinueClicked = onConfirmRecoveryKeyClicked, + onDismissClicked = { eventSink(RoomListEvents.DismissRecoveryKeyPrompt) } + ) + } + } + else -> Unit + } + + if (state.invitesState != InvitesState.NoInvites) { + item { + InvitesEntryPointView(onInvitesClicked, state.invitesState) + } + } + // Note: do not use a key for the LazyColumn, or the scroll will not behave as expected if a room + // is moved to the top of the list. + itemsIndexed( + items = state.summaries, + contentType = { _, room -> room.contentType() }, + ) { index, room -> + RoomSummaryRow( + room = room, + onClick = onRoomClicked, + onLongClick = onRoomLongClicked, + ) + if (index != state.summaries.lastIndex) { + HorizontalDivider() + } + } + } +} + +@Composable +private fun EmptyViewForFilterStates( + selectedFilters: ImmutableList, + modifier: Modifier = Modifier, +) { + val emptyStateResources = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) ?: return + EmptyScaffold( + title = emptyStateResources.title, + subtitle = emptyStateResources.subtitle, + modifier = modifier, + ) +} + +@Composable +private fun EmptyScaffold( + @StringRes title: Int, + @StringRes subtitle: Int, + modifier: Modifier = Modifier, + action: @Composable (ColumnScope.() -> Unit)? = null, +) { + Column( + modifier = modifier.padding(horizontal = 60.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(title), + style = ElementTheme.typography.fontHeadingMdBold, + color = ElementTheme.colors.textPrimary, + textAlign = TextAlign.Center, + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = stringResource(subtitle), + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textSecondary, + textAlign = TextAlign.Center, + ) + Spacer(modifier = Modifier.height(32.dp)) + action?.invoke(this) + } +} + +@PreviewsDayNight +@Composable +internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStateProvider::class) state: RoomListContentState) = ElementPreview { + RoomListContentView( + contentState = state, + filtersState = aRoomListFiltersState( + filterSelectionStates = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = true) } + ), + eventSink = {}, + onVerifyClicked = { }, + onConfirmRecoveryKeyClicked = { }, + onRoomClicked = {}, + onRoomLongClicked = {}, + onCreateRoomClicked = { }, + onInvitesClicked = { }) +} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 0d0a750a9f..50b4144225 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -17,6 +17,7 @@ package io.element.android.features.roomlist.impl.components import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -40,7 +41,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalInspectionMode @@ -52,8 +52,12 @@ import io.element.android.appconfig.RoomListConfig import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.roomlist.impl.R +import io.element.android.features.roomlist.impl.filters.RoomListFiltersState +import io.element.android.features.roomlist.impl.filters.RoomListFiltersView +import io.element.android.features.roomlist.impl.filters.aRoomListFiltersState import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom 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.components.avatarBloom import io.element.android.libraries.designsystem.preview.ElementPreview @@ -91,6 +95,8 @@ fun RoomListTopBar( onOpenSettings: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, displayMenuItems: Boolean, + displayFilters: Boolean, + filtersState: RoomListFiltersState, modifier: Modifier = Modifier, ) { DefaultRoomListTopBar( @@ -102,6 +108,8 @@ fun RoomListTopBar( onMenuActionClicked = onMenuActionClicked, scrollBehavior = scrollBehavior, displayMenuItems = displayMenuItems, + displayFilters = displayFilters, + filtersState = filtersState, modifier = modifier, ) } @@ -117,6 +125,8 @@ private fun DefaultRoomListTopBar( onSearchClicked: () -> Unit, onMenuActionClicked: (RoomListMenuAction) -> Unit, displayMenuItems: Boolean, + displayFilters: Boolean, + filtersState: RoomListFiltersState, modifier: Modifier = Modifier, ) { // We need this to manually clip the top app bar in preview mode @@ -153,12 +163,11 @@ private fun DefaultRoomListTopBar( titleLarge = collapsedTitleTextStyle ), ) { - MediumTopAppBar( + Column( modifier = Modifier .onSizeChanged { appBarHeight = it.height } - .nestedScroll(scrollBehavior.nestedScrollConnection) .avatarBloom( avatarData = avatarData, background = if (ElementTheme.isLightTheme) { @@ -178,113 +187,104 @@ private fun DefaultRoomListTopBar( DpSize.Unspecified }, bottomSoftEdgeColor = ElementTheme.materialColors.background, - bottomSoftEdgeAlpha = 1f - collapsedFraction, + bottomSoftEdgeAlpha = if (displayFilters) { + 1f + } else { + 1f - collapsedFraction + }, alpha = if (areSearchResultsDisplayed) 0f else 1f, ) .statusBarsPadding(), - colors = TopAppBarDefaults.mediumTopAppBarColors( - containerColor = Color.Transparent, - scrolledContainerColor = Color.Transparent, - ), - title = { - Text(text = stringResource(id = R.string.screen_roomlist_main_space_title)) - }, - navigationIcon = { - IconButton( - modifier = Modifier.testTag(TestTags.homeScreenSettings), - onClick = onOpenSettings - ) { - if (avatarData != null) { - Avatar( - avatarData = avatarData!!, - contentDescription = stringResource(CommonStrings.common_settings), - ) - } else { - // Placeholder avatar until the avatarData is available - Surface( - modifier = Modifier.size(AvatarSize.CurrentUserTopBar.dp), - shape = CircleShape, - color = ElementTheme.colors.iconSecondary, - content = {} - ) - } - if (showAvatarIndicator) { - RedIndicatorAtom( - modifier = Modifier - .padding(4.5.dp) - .align(Alignment.TopEnd) - ) - } - } - }, - actions = { - if (displayMenuItems) { - IconButton( - onClick = onSearchClicked, - ) { - Icon( - imageVector = CompoundIcons.Search(), - contentDescription = stringResource(CommonStrings.action_search), - ) - } - if (RoomListConfig.HAS_DROP_DOWN_MENU) { - var showMenu by remember { mutableStateOf(false) } + ) { + MediumTopAppBar( + colors = TopAppBarDefaults.mediumTopAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = Color.Transparent, + ), + title = { + Text(text = stringResource(id = R.string.screen_roomlist_main_space_title)) + }, + navigationIcon = { + NavigationIcon( + avatarData = avatarData, + showAvatarIndicator = showAvatarIndicator, + onClick = onOpenSettings, + ) + }, + actions = { + if (displayMenuItems) { IconButton( - onClick = { showMenu = !showMenu } + onClick = onSearchClicked, ) { Icon( - imageVector = CompoundIcons.OverflowVertical(), - contentDescription = null, + imageVector = CompoundIcons.Search(), + contentDescription = stringResource(CommonStrings.action_search), ) } - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false } - ) { - if (RoomListConfig.SHOW_INVITE_MENU_ITEM) { - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.InviteFriends) - }, - text = { Text(stringResource(id = CommonStrings.action_invite)) }, - leadingIcon = { - Icon( - imageVector = CompoundIcons.ShareAndroid(), - tint = ElementTheme.materialColors.secondary, - contentDescription = null, - ) - } + if (RoomListConfig.HAS_DROP_DOWN_MENU) { + var showMenu by remember { mutableStateOf(false) } + IconButton( + onClick = { showMenu = !showMenu } + ) { + Icon( + imageVector = CompoundIcons.OverflowVertical(), + contentDescription = null, ) } - if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) { - DropdownMenuItem( - onClick = { - showMenu = false - onMenuActionClicked(RoomListMenuAction.ReportBug) - }, - text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, - leadingIcon = { - Icon( - imageVector = CompoundIcons.ChatProblem(), - tint = ElementTheme.materialColors.secondary, - contentDescription = null, - ) - } - ) + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + if (RoomListConfig.SHOW_INVITE_MENU_ITEM) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.InviteFriends) + }, + text = { Text(stringResource(id = CommonStrings.action_invite)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ShareAndroid(), + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } + if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) { + DropdownMenuItem( + onClick = { + showMenu = false + onMenuActionClicked(RoomListMenuAction.ReportBug) + }, + text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, + leadingIcon = { + Icon( + imageVector = CompoundIcons.ChatProblem(), + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } } } } - } - }, - scrollBehavior = scrollBehavior, - windowInsets = WindowInsets(0.dp), - ) + }, + scrollBehavior = scrollBehavior, + windowInsets = WindowInsets(0.dp), + ) + if (displayFilters) { + RoomListFiltersView( + state = filtersState, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + } } HorizontalDivider( - modifier = - Modifier + modifier = Modifier .fillMaxWidth() .alpha(collapsedFraction) .align(Alignment.BottomCenter), @@ -293,6 +293,40 @@ private fun DefaultRoomListTopBar( } } +@Composable +private fun NavigationIcon( + avatarData: AvatarData?, + showAvatarIndicator: Boolean, + onClick: () -> Unit, +) { + IconButton( + modifier = Modifier.testTag(TestTags.homeScreenSettings), + onClick = onClick, + ) { + Box { + if (avatarData != null) { + Avatar( + avatarData = avatarData, + contentDescription = stringResource(CommonStrings.common_settings), + ) + } else { + // Placeholder avatar until the avatarData is available + Surface( + modifier = Modifier.size(AvatarSize.CurrentUserTopBar.dp), + shape = CircleShape, + color = ElementTheme.colors.iconSecondary, + content = {} + ) + } + if (showAvatarIndicator) { + RedIndicatorAtom( + modifier = Modifier.align(Alignment.TopEnd) + ) + } + } + } +} + @OptIn(ExperimentalMaterial3Api::class) @PreviewsDayNight @Composable @@ -305,6 +339,8 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview { onOpenSettings = {}, onSearchClicked = {}, displayMenuItems = true, + displayFilters = true, + filtersState = aRoomListFiltersState(), onMenuActionClicked = {}, ) } @@ -321,6 +357,8 @@ internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview { onOpenSettings = {}, onSearchClicked = {}, displayMenuItems = true, + displayFilters = true, + filtersState = aRoomListFiltersState(), onMenuActionClicked = {}, ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index cb61701342..2f078d7a69 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -27,12 +27,11 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext @@ -62,19 +61,15 @@ class RoomListDataSource @Inject constructor( roomListService .allRooms .summaries - .onStart { - // If we have no cached results, display a placeholder loading state - if (diffCache.isEmpty()) { - _allRooms.emit(RoomListRoomSummaryFactory.createFakeList()) - } - } .onEach { roomSummaries -> replaceWith(roomSummaries) } .launchIn(coroutineScope) } - val allRooms: SharedFlow> = _allRooms + val allRooms: Flow> = _allRooms + + val loadingState = roomListService.allRooms.loadingState @OptIn(FlowPreview::class) private fun observeNotificationSettings() { 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 02f87de745..07cdabea8e 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 @@ -24,8 +24,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject class RoomListRoomSummaryFactory @Inject constructor( @@ -52,12 +50,6 @@ class RoomListRoomSummaryFactory @Inject constructor( isFavorite = false, ) } - - fun createFakeList(): ImmutableList { - return List(16) { - createPlaceholder("!fakeRoom$it:domain") - }.toImmutableList() - } } fun create(roomSummary: RoomSummary.Filled): RoomListRoomSummary { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt index b66401695e..7176ddef1e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt @@ -21,6 +21,8 @@ import dagger.Binds import dagger.Module import io.element.android.features.roomlist.impl.filters.RoomListFiltersPresenter import io.element.android.features.roomlist.impl.filters.RoomListFiltersState +import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter +import io.element.android.features.roomlist.impl.migration.MigrationScreenState import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.libraries.architecture.Presenter @@ -34,4 +36,7 @@ interface RoomListModule { @Binds fun bindFiltersPresenter(presenter: RoomListFiltersPresenter): Presenter + + @Binds + fun bindMigrationScreenPresenter(presenter: MigrationScreenPresenter): Presenter } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFilter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFilter.kt index 31405c45e7..51b9570c6d 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFilter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFilter.kt @@ -20,12 +20,12 @@ import io.element.android.features.roomlist.impl.R /** * Enum class representing the different filters that can be applied to the room list. - * Order is important. + * Order is important, it'll be used as initial order in the UI. */ enum class RoomListFilter(val stringResource: Int) { - Rooms(R.string.screen_roomlist_filter_rooms), - People(R.string.screen_roomlist_filter_people), Unread(R.string.screen_roomlist_filter_unreads), + People(R.string.screen_roomlist_filter_people), + Rooms(R.string.screen_roomlist_filter_rooms), Favourites(R.string.screen_roomlist_filter_favourites); val oppositeFilter: RoomListFilter? diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResources.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResources.kt new file mode 100644 index 0000000000..53763abd0d --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResources.kt @@ -0,0 +1,65 @@ +/* + * 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.roomlist.impl.filters + +import androidx.annotation.StringRes +import io.element.android.features.roomlist.impl.R + +/** + * Holds the resources for the empty state when filters are applied to the room list. + * @param title the title of the empty state + * @param subtitle the subtitle of the empty state + */ +data class RoomListFiltersEmptyStateResources( + @StringRes val title: Int, + @StringRes val subtitle: Int, +) { + companion object { + /** + * Create a [RoomListFiltersEmptyStateResources] from a list of selected filters. + */ + fun fromSelectedFilters(selectedFilters: List): RoomListFiltersEmptyStateResources? { + return when { + selectedFilters.isEmpty() -> null + selectedFilters.size == 1 -> { + when (selectedFilters.first()) { + RoomListFilter.Unread -> RoomListFiltersEmptyStateResources( + title = R.string.screen_roomlist_filter_unreads_empty_state_title, + subtitle = R.string.screen_roomlist_filter_mixed_empty_state_subtitle + ) + RoomListFilter.People -> RoomListFiltersEmptyStateResources( + title = R.string.screen_roomlist_filter_people_empty_state_title, + subtitle = R.string.screen_roomlist_filter_mixed_empty_state_subtitle + ) + RoomListFilter.Rooms -> RoomListFiltersEmptyStateResources( + title = R.string.screen_roomlist_filter_rooms_empty_state_title, + subtitle = R.string.screen_roomlist_filter_mixed_empty_state_subtitle + ) + RoomListFilter.Favourites -> RoomListFiltersEmptyStateResources( + title = R.string.screen_roomlist_filter_favourites_empty_state_title, + subtitle = R.string.screen_roomlist_filter_favourites_empty_state_subtitle + ) + } + } + else -> RoomListFiltersEmptyStateResources( + title = R.string.screen_roomlist_filter_mixed_empty_state_title, + subtitle = R.string.screen_roomlist_filter_mixed_empty_state_subtitle + ) + } + } + } +} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEvents.kt index d243ea7ca0..c7855444ef 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEvents.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEvents.kt @@ -17,6 +17,6 @@ package io.element.android.features.roomlist.impl.filters sealed interface RoomListFiltersEvents { - data object ClearSelectedFilters : RoomListFiltersEvents data class ToggleFilter(val filter: RoomListFilter) : RoomListFiltersEvents + data object ClearSelectedFilters : RoomListFiltersEvents } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt index 090b4e868a..27edc91627 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt @@ -20,9 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionStrategy import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags @@ -34,50 +32,36 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListFilter as Matrix class RoomListFiltersPresenter @Inject constructor( private val roomListService: RoomListService, private val featureFlagService: FeatureFlagService, + private val filterSelectionStrategy: FilterSelectionStrategy, ) : Presenter { @Composable override fun present(): RoomListFiltersState { val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.RoomListFilters).collectAsState(false) - var unselectedFilters: Set by rememberSaveable { - mutableStateOf(RoomListFilter.entries.toSet()) - } - var selectedFilters: Set by rememberSaveable { - mutableStateOf(emptySet()) - } - - fun updateFilters(newSelectedFilters: Set) { - selectedFilters = newSelectedFilters - unselectedFilters = RoomListFilter.entries.toSet() - - selectedFilters - - selectedFilters.mapNotNull { it.oppositeFilter }.toSet() - } + val filters by filterSelectionStrategy.filterSelectionStates.collectAsState() fun handleEvents(event: RoomListFiltersEvents) { when (event) { - is RoomListFiltersEvents.ToggleFilter -> { - val newSelectedFilters = if (selectedFilters.contains(event.filter)) { - selectedFilters - event.filter - } else { - selectedFilters + event.filter - } - updateFilters(newSelectedFilters) - } RoomListFiltersEvents.ClearSelectedFilters -> { - updateFilters(newSelectedFilters = emptySet()) + filterSelectionStrategy.clear() + } + is RoomListFiltersEvents.ToggleFilter -> { + filterSelectionStrategy.toggle(event.filter) } } } LaunchedEffect(isFeatureEnabled) { if (!isFeatureEnabled) { - updateFilters(emptySet()) + filterSelectionStrategy.clear() } } - LaunchedEffect(selectedFilters) { + LaunchedEffect(filters) { val allRoomsFilter = MatrixRoomListFilter.All( - selectedFilters.map { roomListFilter -> - when (roomListFilter) { + filters + .filter { it.isSelected } + .map { roomListFilter -> + when (roomListFilter.filter) { RoomListFilter.Rooms -> MatrixRoomListFilter.Category.Group RoomListFilter.People -> MatrixRoomListFilter.Category.People RoomListFilter.Unread -> MatrixRoomListFilter.Unread @@ -89,8 +73,7 @@ class RoomListFiltersPresenter @Inject constructor( } return RoomListFiltersState( - unselectedFilters = unselectedFilters.toPersistentList(), - selectedFilters = selectedFilters.toPersistentList(), + filterSelectionStates = filters.toPersistentList(), isFeatureEnabled = isFeatureEnabled, eventSink = ::handleEvents ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersState.kt index e496336742..14850ef82e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersState.kt @@ -16,13 +16,21 @@ package io.element.android.features.roomlist.impl.filters +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionState import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toPersistentList data class RoomListFiltersState( - val unselectedFilters: ImmutableList, - val selectedFilters: ImmutableList, + val filterSelectionStates: ImmutableList, val isFeatureEnabled: Boolean, val eventSink: (RoomListFiltersEvents) -> Unit, ) { - val hasAnyFilterSelected = selectedFilters.isNotEmpty() + val hasAnyFilterSelected = filterSelectionStates.any { it.isSelected } + + fun selectedFilters(): ImmutableList { + return filterSelectionStates + .filter { it.isSelected } + .map { it.filter } + .toPersistentList() + } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersStateProvider.kt index 281f014cc7..00d1352728 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersStateProvider.kt @@ -17,8 +17,7 @@ package io.element.android.features.roomlist.impl.filters import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionState import kotlinx.collections.immutable.toImmutableList class RoomListFiltersStateProvider : PreviewParameterProvider { @@ -26,20 +25,17 @@ class RoomListFiltersStateProvider : PreviewParameterProvider = RoomListFilter.entries.toImmutableList(), - selectedFilters: ImmutableList = persistentListOf(), + filterSelectionStates: List = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = false) }, isFeatureEnabled: Boolean = true, eventSink: (RoomListFiltersEvents) -> Unit = {}, ) = RoomListFiltersState( - unselectedFilters = unselectedFilters, - selectedFilters = selectedFilters, + filterSelectionStates = filterSelectionStates.toImmutableList(), isFeatureEnabled = isFeatureEnabled, eventSink = eventSink, ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt index efabd5cf28..fcdc260c6e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt @@ -16,24 +16,22 @@ package io.element.android.features.roomlist.impl.filters -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults -import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -42,17 +40,14 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.libraries.designsystem.modifiers.fadingEdge -import io.element.android.libraries.designsystem.modifiers.horizontalFadingEdgesBrush 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.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import kotlinx.collections.immutable.ImmutableList +@OptIn(ExperimentalFoundationApi::class) @Composable fun RoomListFiltersView( state: RoomListFiltersState, @@ -62,53 +57,45 @@ fun RoomListFiltersView( state.eventSink(RoomListFiltersEvents.ClearSelectedFilters) } - fun onFilterClicked(filter: RoomListFilter) { + fun onToggleFilter(filter: RoomListFilter) { state.eventSink(RoomListFiltersEvents.ToggleFilter(filter)) } - val startPadding = if (state.hasAnyFilterSelected) 4.dp else 16.dp - Row( - modifier = modifier.padding(start = startPadding, end = 16.dp), + val lazyListState = rememberLazyListState() + LazyRow( + contentPadding = PaddingValues(start = 8.dp, end = 16.dp), + modifier = modifier.fillMaxWidth(), + state = lazyListState, + horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { - AnimatedVisibility(visible = state.hasAnyFilterSelected) { - RoomListClearFiltersButton( - modifier = Modifier.testTag(TestTags.homeScreenClearFilters), - onClick = ::onClearFiltersClicked - ) + item("clear_filters") { + if (state.hasAnyFilterSelected) { + RoomListClearFiltersButton( + modifier = Modifier + .padding(start = 8.dp) + .testTag(TestTags.homeScreenClearFilters), + onClick = ::onClearFiltersClicked + ) + } } - val lazyListState = rememberLazyListState() - val fadingEdgesBrush = horizontalFadingEdgesBrush( - showLeft = lazyListState.canScrollBackward, - showRight = lazyListState.canScrollForward - ) - LazyRow( - modifier = Modifier - .fillMaxWidth() - .fadingEdge(fadingEdgesBrush), - state = lazyListState, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - roomListFilters(state.selectedFilters, selected = true, onClick = ::onFilterClicked) - roomListFilters(state.unselectedFilters, selected = false, onClick = ::onFilterClicked) + for (filterWithSelection in state.filterSelectionStates) { + item(filterWithSelection.filter) { + RoomListFilterView( + modifier = Modifier.animateItemPlacement(), + roomListFilter = filterWithSelection.filter, + selected = filterWithSelection.isSelected, + onClick = ::onToggleFilter, + ) + } } } -} - -@OptIn(ExperimentalFoundationApi::class) -private fun LazyListScope.roomListFilters( - filters: ImmutableList, - selected: Boolean, - onClick: (RoomListFilter) -> Unit, -) { - items( - items = filters, - ) { filter -> - RoomListFilterView( - roomListFilter = filter, - selected = selected, - onClick = onClick, - ) + LaunchedEffect(state.filterSelectionStates) { + // Checking for canScrollBackward is necessary for the itemPlacementAnimation to work correctly. + // We don't want the itemPlacementAnimation to be triggered when clearing the filters. + if (!state.hasAnyFilterSelected || lazyListState.canScrollBackward) { + lazyListState.animateScrollToItem(0) + } } } @@ -117,22 +104,18 @@ private fun RoomListClearFiltersButton( onClick: () -> Unit, modifier: Modifier = Modifier ) { - IconButton( - modifier = modifier, - onClick = onClick, + Box( + modifier = modifier + .clip(CircleShape) + .background(ElementTheme.colors.bgActionPrimaryRest) + .clickable(onClick = onClick) ) { - Box( - modifier = Modifier - .clip(CircleShape) - .background(ElementTheme.colors.bgActionPrimaryRest) - ) { - Icon( - modifier = Modifier.align(Alignment.Center), - imageVector = CompoundIcons.Close(), - tint = ElementTheme.colors.iconOnSolidPrimary, - contentDescription = stringResource(id = io.element.android.libraries.ui.strings.R.string.action_clear), - ) - } + Icon( + modifier = Modifier.align(Alignment.Center), + imageVector = CompoundIcons.Close(), + tint = ElementTheme.colors.iconOnSolidPrimary, + contentDescription = stringResource(id = io.element.android.libraries.ui.strings.R.string.action_clear), + ) } } @@ -146,9 +129,7 @@ private fun RoomListFilterView( FilterChip( selected = selected, onClick = { onClick(roomListFilter) }, - modifier = modifier - .minimumInteractiveComponentSize() - .height(36.dp), + modifier = modifier.height(36.dp), shape = CircleShape, colors = FilterChipDefaults.filterChipColors( containerColor = ElementTheme.colors.bgCanvasDefault, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/DefaultFilterSelectionStrategy.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/DefaultFilterSelectionStrategy.kt new file mode 100644 index 0000000000..d56c84a572 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/DefaultFilterSelectionStrategy.kt @@ -0,0 +1,66 @@ +/* + * 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.roomlist.impl.filters.selection + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.roomlist.impl.filters.RoomListFilter +import io.element.android.libraries.di.SessionScope +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +class DefaultFilterSelectionStrategy @Inject constructor() : FilterSelectionStrategy { + private val selectedFilters = LinkedHashSet() + + override val filterSelectionStates = MutableStateFlow(buildFilters()) + + override fun select(filter: RoomListFilter) { + selectedFilters.add(filter) + filterSelectionStates.value = buildFilters() + } + + override fun deselect(filter: RoomListFilter) { + selectedFilters.remove(filter) + filterSelectionStates.value = buildFilters() + } + + override fun isSelected(filter: RoomListFilter): Boolean { + return selectedFilters.contains(filter) + } + + override fun clear() { + selectedFilters.clear() + filterSelectionStates.value = buildFilters() + } + + private fun buildFilters(): Set { + val selectedFilterStates = selectedFilters.map { + FilterSelectionState( + filter = it, + isSelected = true + ) + } + val unselectedFilters = RoomListFilter.entries - selectedFilters - selectedFilters.mapNotNull { it.oppositeFilter }.toSet() + val unselectedFilterStates = unselectedFilters.map { + FilterSelectionState( + filter = it, + isSelected = false + ) + } + return (selectedFilterStates + unselectedFilterStates).toSet() + } +} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionState.kt new file mode 100644 index 0000000000..431eee7257 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionState.kt @@ -0,0 +1,24 @@ +/* + * 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.roomlist.impl.filters.selection + +import io.element.android.features.roomlist.impl.filters.RoomListFilter + +data class FilterSelectionState( + val filter: RoomListFilter, + val isSelected: Boolean, +) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionStrategy.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionStrategy.kt new file mode 100644 index 0000000000..415c67fc7b --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/selection/FilterSelectionStrategy.kt @@ -0,0 +1,37 @@ +/* + * 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.roomlist.impl.filters.selection + +import io.element.android.features.roomlist.impl.filters.RoomListFilter +import kotlinx.coroutines.flow.StateFlow + +interface FilterSelectionStrategy { + val filterSelectionStates: StateFlow> + + fun select(filter: RoomListFilter) + fun deselect(filter: RoomListFilter) + fun isSelected(filter: RoomListFilter): Boolean + fun clear() + + fun toggle(filter: RoomListFilter) { + if (isSelected(filter)) { + deselect(filter) + } else { + select(filter) + } + } +} 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 4e83e681fd..beffffa8ef 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 @@ -33,8 +33,7 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.filters.RoomListFiltersState import io.element.android.features.roomlist.impl.filters.aRoomListFiltersState -import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore -import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter +import io.element.android.features.roomlist.impl.migration.MigrationScreenState import io.element.android.features.roomlist.impl.model.createRoomListRoomSummary import io.element.android.features.roomlist.impl.search.RoomListSearchEvents import io.element.android.features.roomlist.impl.search.RoomListSearchState @@ -54,13 +53,12 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -75,6 +73,7 @@ import io.element.android.libraries.matrix.test.verification.FakeSessionVerifica import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.MutablePresenter import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.testCoroutineDispatchers @@ -86,7 +85,6 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import kotlin.time.Duration.Companion.seconds class RoomListPresenterTests { @get:Rule @@ -127,7 +125,6 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() assertThat(initialState.showAvatarIndicator).isTrue() sessionVerificationService.givenCanVerifySession(false) @@ -169,11 +166,9 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = consumeItemsUntilPredicate(timeout = 3.seconds) { state -> state.roomList.dataOrNull()?.size == 16 }.last() - // Room list is loaded with 16 placeholders - val initialItems = initialState.roomList.dataOrNull().orEmpty() - assertThat(initialItems.size).isEqualTo(16) - assertThat(initialItems.all { it.isPlaceholder }).isTrue() + val initialState = consumeItemsUntilPredicate { state -> state.contentState is RoomListContentState.Skeleton }.last() + assertThat(initialState.contentState).isInstanceOf(RoomListContentState.Skeleton::class.java) + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) roomListService.postAllRooms( listOf( aRoomSummaryFilled( @@ -182,10 +177,10 @@ class RoomListPresenterTests { ) ) ) - val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.dataOrNull()?.size == 1 }.last() - val withRoomStateItems = withRoomState.roomList.dataOrNull().orEmpty() - assertThat(withRoomStateItems.size).isEqualTo(1) - assertThat(withRoomStateItems.first()).isEqualTo( + val withRoomsState = + consumeItemsUntilPredicate { state -> state.contentState is RoomListContentState.Rooms && state.contentAsRooms().summaries.isNotEmpty() }.last() + assertThat(withRoomsState.contentAsRooms().summaries).hasSize(1) + assertThat(withRoomsState.contentAsRooms().summaries.first()).isEqualTo( createRoomListRoomSummary( numberOfUnreadMentions = 1, numberOfUnreadMessages = 2, @@ -241,23 +236,28 @@ class RoomListPresenterTests { @Test fun `present - handle RecoveryKeyConfirmation last session`() = runTest { val scope = CoroutineScope(context = coroutineContext + SupervisorJob()) + val roomListService = FakeRoomListService().apply { + postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) + } val presenter = createRoomListPresenter( coroutineScope = scope, client = FakeMatrixClient( encryptionService = FakeEncryptionService().apply { emitIsLastDevice(true) - } + }, + roomListService = roomListService ), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val eventSink = awaitItem().eventSink + val eventSink = consumeItemsUntilPredicate { + it.contentState is RoomListContentState.Rooms + }.last().eventSink // For the last session, the state is not SessionVerification, but RecoveryKeyConfirmation - assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) eventSink(RoomListEvents.DismissRequestVerificationPrompt) - assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.None) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) scope.cancel() } } @@ -265,16 +265,22 @@ class RoomListPresenterTests { @Test fun `present - handle DismissRequestVerificationPrompt`() = runTest { val scope = CoroutineScope(context = coroutineContext + SupervisorJob()) + val roomListService = FakeRoomListService().apply { + postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) + } val presenter = createRoomListPresenter( + client = FakeMatrixClient(roomListService = roomListService), coroutineScope = scope, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val eventSink = awaitItem().eventSink - assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.SessionVerification) + val eventSink = consumeItemsUntilPredicate { + it.contentState is RoomListContentState.Rooms + }.last().eventSink + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.SessionVerification) eventSink(RoomListEvents.DismissRequestVerificationPrompt) - assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.None) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) scope.cancel() } } @@ -282,7 +288,11 @@ class RoomListPresenterTests { @Test fun `present - handle DismissRecoveryKeyPrompt`() = runTest { val encryptionService = FakeEncryptionService() + val roomListService = FakeRoomListService().apply { + postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) + } val matrixClient = FakeMatrixClient( + roomListService = roomListService, encryptionService = encryptionService, sessionVerificationService = FakeSessionVerificationService().apply { givenCanVerifySession(false) @@ -297,15 +307,16 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - assertThat(initialState.securityBannerState).isEqualTo(SecurityBannerState.None) + val initialState = consumeItemsUntilPredicate { + it.contentState is RoomListContentState.Rooms + }.last() + assertThat(initialState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE) val nextState = awaitItem() - assertThat(nextState.securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) + assertThat(nextState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) nextState.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) val finalState = awaitItem() - assertThat(finalState.securityBannerState).isEqualTo(SecurityBannerState.None) + assertThat(finalState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) scope.cancel() } } @@ -314,22 +325,30 @@ class RoomListPresenterTests { fun `present - sets invite state`() = runTest { val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites) val inviteStateDataSource = FakeInviteDataSource(inviteStateFlow) + val roomListService = FakeRoomListService() val scope = CoroutineScope(coroutineContext + SupervisorJob()) - val presenter = createRoomListPresenter(inviteStateDataSource = inviteStateDataSource, coroutineScope = scope) + val presenter = createRoomListPresenter( + inviteStateDataSource = inviteStateDataSource, + coroutineScope = scope, + client = FakeMatrixClient(roomListService = roomListService), + ) + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) + consumeItemsUntilPredicate { + it.contentState is RoomListContentState.Rooms + } + assertThat(awaitItem().contentAsRooms().invitesState).isEqualTo(InvitesState.NoInvites) inviteStateFlow.value = InvitesState.SeenInvites - assertThat(awaitItem().invitesState).isEqualTo(InvitesState.SeenInvites) + assertThat(awaitItem().contentAsRooms().invitesState).isEqualTo(InvitesState.SeenInvites) inviteStateFlow.value = InvitesState.NewInvites - assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NewInvites) + assertThat(awaitItem().contentAsRooms().invitesState).isEqualTo(InvitesState.NewInvites) inviteStateFlow.value = InvitesState.NoInvites - assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites) + assertThat(awaitItem().contentAsRooms().invitesState).isEqualTo(InvitesState.NoInvites) scope.cancel() } } @@ -477,6 +496,7 @@ class RoomListPresenterTests { val userDefinedMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY val notificationSettingsService = FakeNotificationSettingsService() val roomListService = FakeRoomListService() + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) roomListService.postAllRooms(listOf(aRoomSummaryFilled(notificationMode = userDefinedMode))) val matrixClient = FakeMatrixClient( roomListService = roomListService, @@ -488,12 +508,13 @@ class RoomListPresenterTests { presenter.present() }.test { notificationSettingsService.setRoomNotificationMode(A_ROOM_ID, userDefinedMode) - val updatedState = consumeItemsUntilPredicate { state -> - state.roomList.dataOrNull().orEmpty().any { it.id == A_ROOM_ID.value && it.userDefinedNotificationMode == userDefinedMode } + (state.contentState as? RoomListContentState.Rooms)?.summaries.orEmpty().any { summary -> + summary.id == A_ROOM_ID.value && summary.userDefinedNotificationMode == userDefinedMode + } }.last() - val room = updatedState.roomList.dataOrNull()?.find { it.id == A_ROOM_ID.value } + val room = updatedState.contentAsRooms().summaries.find { it.id == A_ROOM_ID.value } assertThat(room?.userDefinedNotificationMode).isEqualTo(userDefinedMode) cancelAndIgnoreRemainingEvents() scope.cancel() @@ -526,30 +547,46 @@ class RoomListPresenterTests { } } - fun `present - change in migration presenter state modifies isMigrating`() = runTest { - val client = FakeMatrixClient(sessionId = A_SESSION_ID) - val migrationStore = InMemoryMigrationScreenStore() - val migrationScreenPresenter = MigrationScreenPresenter(client, migrationStore) + @Test + fun `present - change in migration presenter state modifies contentState`() = runTest { + val migrationScreenPresenter = MutablePresenter(MigrationScreenState(true)) val scope = CoroutineScope(coroutineContext + SupervisorJob()) val presenter = createRoomListPresenter( - client = client, coroutineScope = scope, migrationScreenPresenter = migrationScreenPresenter, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() // The migration screen is shown if the migration screen has not been shown before - assertThat(initialState.displayMigrationStatus).isTrue() - skipItems(2) - + assertThat(initialState.contentState).isInstanceOf(RoomListContentState.Migration::class.java) // Set migration as done and set the room list service as running to trigger a refresh of the presenter value - (client.roomListService as FakeRoomListService).postState(RoomListService.State.Running) - migrationStore.setMigrationScreenShown(A_SESSION_ID) - + migrationScreenPresenter.updateState(MigrationScreenState(false)) // The migration screen is not shown anymore - assertThat(awaitItem().displayMigrationStatus).isFalse() + assertThat(awaitItem().contentState).isInstanceOf(RoomListContentState.Skeleton::class.java) + scope.cancel() + } + } + + @Test + fun `present - when room service returns no room, then contentState is Empty `() = runTest { + val scope = CoroutineScope(coroutineContext + SupervisorJob()) + val roomListService = FakeRoomListService() + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(0)) + val matrixClient = FakeMatrixClient( + roomListService = roomListService, + ) + val presenter = createRoomListPresenter( + client = matrixClient, + coroutineScope = scope, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + assertThat(awaitItem().contentState).isInstanceOf(RoomListContentState.Empty::class.java) scope.cancel() } } @@ -609,10 +646,7 @@ class RoomListPresenterTests { sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), featureFlagService: FeatureFlagService = FakeFeatureFlagService(), coroutineScope: CoroutineScope, - migrationScreenPresenter: MigrationScreenPresenter = MigrationScreenPresenter( - matrixClient = client, - migrationScreenStore = InMemoryMigrationScreenStore(), - ), + migrationScreenPresenter: Presenter = Presenter { MigrationScreenState(false) }, analyticsService: AnalyticsService = FakeAnalyticsService(), filtersPresenter: Presenter = Presenter { aRoomListFiltersState() }, searchPresenter: Presenter = Presenter { aRoomListSearchState() }, diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt new file mode 100644 index 0000000000..27c4f33f1b --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -0,0 +1,19 @@ +/* + * 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.roomlist.impl + +internal fun RoomListState.contentAsRooms() = contentState as RoomListContentState.Rooms diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt index 347ff65687..f545b27860 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomlist.impl.components.RoomListMenuAction -import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -35,7 +34,6 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam -import kotlinx.collections.immutable.persistentListOf import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule @@ -50,7 +48,7 @@ class RoomListViewTest { val eventsRecorder = EventsRecorder() rule.setRoomListView( state = aRoomListState( - securityBannerState = SecurityBannerState.SessionVerification, + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SessionVerification), eventSink = eventsRecorder, ) ) @@ -65,7 +63,7 @@ class RoomListViewTest { ensureCalledOnce { callback -> rule.setRoomListView( state = aRoomListState( - securityBannerState = SecurityBannerState.SessionVerification, + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SessionVerification), eventSink = eventsRecorder, ), onVerifyClicked = callback, @@ -79,7 +77,7 @@ class RoomListViewTest { val eventsRecorder = EventsRecorder() rule.setRoomListView( state = aRoomListState( - securityBannerState = SecurityBannerState.RecoveryKeyConfirmation, + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), eventSink = eventsRecorder, ) ) @@ -94,7 +92,7 @@ class RoomListViewTest { ensureCalledOnce { callback -> rule.setRoomListView( state = aRoomListState( - securityBannerState = SecurityBannerState.RecoveryKeyConfirmation, + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), eventSink = eventsRecorder, ), onConfirmRecoveryKeyClicked = callback, @@ -110,7 +108,7 @@ class RoomListViewTest { rule.setRoomListView( state = aRoomListState( eventSink = eventsRecorder, - roomList = AsyncData.Success(persistentListOf()), + contentState = anEmptyContentState(), ), onCreateRoomClicked = callback, ) @@ -124,7 +122,7 @@ class RoomListViewTest { val state = aRoomListState( eventSink = eventsRecorder, ) - val room0 = state.roomList.dataOrNull()!!.first() + val room0 = state.contentAsRooms().summaries.first() ensureCalledOnceWithParam(room0.roomId) { callback -> rule.setRoomListView( state = state, @@ -140,7 +138,7 @@ class RoomListViewTest { val state = aRoomListState( eventSink = eventsRecorder, ) - val room0 = state.roomList.dataOrNull()!!.first() + val room0 = state.contentAsRooms().summaries.first() rule.setRoomListView( state = state, ) @@ -170,7 +168,7 @@ class RoomListViewTest { fun `clicking on invites invokes the expected callback`() { val eventsRecorder = EventsRecorder() val state = aRoomListState( - invitesState = InvitesState.NewInvites, + contentState = aRoomsContentState(invitesState = InvitesState.NewInvites), eventSink = eventsRecorder, ) ensureCalledOnce { callback -> diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResourcesTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResourcesTest.kt new file mode 100644 index 0000000000..b9cdf5a03f --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersEmptyStateResourcesTest.kt @@ -0,0 +1,75 @@ +/* + * 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.roomlist.impl.filters + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomlist.impl.R +import org.junit.Test + +class RoomListFiltersEmptyStateResourcesTest { + @Test + fun `fromSelectedFilters should return null when selectedFilters is empty`() { + val selectedFilters = emptyList() + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNull() + } + + @Test + fun `fromSelectedFilters should return exact RoomListFiltersEmptyStateResources when selectedFilters has only unread filter`() { + val selectedFilters = listOf(RoomListFilter.Unread) + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo(R.string.screen_roomlist_filter_unreads_empty_state_title) + assertThat(result?.subtitle).isEqualTo(R.string.screen_roomlist_filter_mixed_empty_state_subtitle) + } + + @Test + fun `fromSelectedFilters should return exact RoomListFiltersEmptyStateResources when selectedFilters has only people filter`() { + val selectedFilters = listOf(RoomListFilter.People) + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo(R.string.screen_roomlist_filter_people_empty_state_title) + assertThat(result?.subtitle).isEqualTo(R.string.screen_roomlist_filter_mixed_empty_state_subtitle) + } + + @Test + fun `fromSelectedFilters should return exact RoomListFiltersEmptyStateResources when selectedFilters has only rooms filter`() { + val selectedFilters = listOf(RoomListFilter.Rooms) + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo(R.string.screen_roomlist_filter_rooms_empty_state_title) + assertThat(result?.subtitle).isEqualTo(R.string.screen_roomlist_filter_mixed_empty_state_subtitle) + } + + @Test + fun `fromSelectedFilters should return exact RoomListFiltersEmptyStateResources when selectedFilters has only favourites filter`() { + val selectedFilters = listOf(RoomListFilter.Favourites) + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo(R.string.screen_roomlist_filter_favourites_empty_state_title) + assertThat(result?.subtitle).isEqualTo(R.string.screen_roomlist_filter_favourites_empty_state_subtitle) + } + + @Test + fun `fromSelectedFilters should return exact RoomListFiltersEmptyStateResources when selectedFilters has multiple filters`() { + val selectedFilters = listOf(RoomListFilter.Unread, RoomListFilter.People, RoomListFilter.Rooms, RoomListFilter.Favourites) + val result = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters) + assertThat(result).isNotNull() + assertThat(result?.title).isEqualTo(R.string.screen_roomlist_filter_mixed_empty_state_title) + assertThat(result?.subtitle).isEqualTo(R.string.screen_roomlist_filter_mixed_empty_state_subtitle) + } +} diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenterTests.kt index 15ee711e82..756fe1aa0e 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenterTests.kt @@ -20,6 +20,8 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomlist.impl.filters.selection.DefaultFilterSelectionStrategy +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionState import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.roomlist.RoomListService @@ -37,13 +39,12 @@ class RoomListFiltersPresenterTests { presenter.present() }.test { awaitItem().let { state -> - assertThat(state.selectedFilters).isEmpty() assertThat(state.hasAnyFilterSelected).isFalse() - assertThat(state.unselectedFilters).containsExactly( - RoomListFilter.Rooms, - RoomListFilter.People, - RoomListFilter.Unread, - RoomListFilter.Favourites, + assertThat(state.filterSelectionStates).containsExactly( + filterSelectionState(RoomListFilter.Unread, false), + filterSelectionState(RoomListFilter.People, false), + filterSelectionState(RoomListFilter.Rooms, false), + filterSelectionState(RoomListFilter.Favourites, false), ) } cancelAndIgnoreRemainingEvents() @@ -58,32 +59,33 @@ class RoomListFiltersPresenterTests { presenter.present() }.test { awaitItem().eventSink.invoke(RoomListFiltersEvents.ToggleFilter(RoomListFilter.Rooms)) - awaitLastSequentialItem().let { state -> - assertThat(state.selectedFilters).containsExactly(RoomListFilter.Rooms) assertThat(state.hasAnyFilterSelected).isTrue() - assertThat(state.unselectedFilters).containsExactly( - RoomListFilter.Unread, - RoomListFilter.Favourites, + assertThat(state.filterSelectionStates).containsExactly( + filterSelectionState(RoomListFilter.Rooms, true), + filterSelectionState(RoomListFilter.Unread, false), + filterSelectionState(RoomListFilter.Favourites, false), + ).inOrder() + + assertThat(state.selectedFilters()).containsExactly( + RoomListFilter.Rooms, ) val roomListCurrentFilter = roomListService.allRooms.currentFilter.value as MatrixRoomListFilter.All assertThat(roomListCurrentFilter.filters).containsExactly( MatrixRoomListFilter.Category.Group, ) - state.eventSink.invoke(RoomListFiltersEvents.ToggleFilter(RoomListFilter.Rooms)) } - awaitLastSequentialItem().let { state -> - assertThat(state.selectedFilters).isEmpty() assertThat(state.hasAnyFilterSelected).isFalse() - assertThat(state.unselectedFilters).containsExactly( - RoomListFilter.Rooms, - RoomListFilter.People, - RoomListFilter.Unread, - RoomListFilter.Favourites, - ) + assertThat(state.filterSelectionStates).containsExactly( + filterSelectionState(RoomListFilter.Unread, false), + filterSelectionState(RoomListFilter.People, false), + filterSelectionState(RoomListFilter.Rooms, false), + filterSelectionState(RoomListFilter.Favourites, false), + ).inOrder() + assertThat(state.selectedFilters()).isEmpty() val roomListCurrentFilter = roomListService.allRooms.currentFilter.value as MatrixRoomListFilter.All assertThat(roomListCurrentFilter.filters).isEmpty() } @@ -99,24 +101,28 @@ class RoomListFiltersPresenterTests { }.test { awaitItem().eventSink.invoke(RoomListFiltersEvents.ToggleFilter(RoomListFilter.Rooms)) awaitLastSequentialItem().let { state -> - assertThat(state.selectedFilters).isNotEmpty() assertThat(state.hasAnyFilterSelected).isTrue() state.eventSink.invoke(RoomListFiltersEvents.ClearSelectedFilters) } awaitLastSequentialItem().let { state -> - assertThat(state.selectedFilters).isEmpty() assertThat(state.hasAnyFilterSelected).isFalse() } } } } -fun createRoomListFiltersPresenter( +private fun filterSelectionState(filter: RoomListFilter, selected: Boolean) = FilterSelectionState( + filter = filter, + isSelected = selected, +) + +private fun createRoomListFiltersPresenter( roomListService: RoomListService = FakeRoomListService(), featureFlagService: FeatureFlagService = FakeFeatureFlagService(), ): RoomListFiltersPresenter { return RoomListFiltersPresenter( roomListService = roomListService, featureFlagService = featureFlagService, + filterSelectionStrategy = DefaultFilterSelectionStrategy(), ) } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersViewTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersViewTests.kt index 6c9bd9e050..94897532e9 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersViewTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersViewTests.kt @@ -20,12 +20,11 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomlist.impl.R +import io.element.android.features.roomlist.impl.filters.selection.FilterSelectionState import io.element.android.libraries.testtags.TestTags import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressTag -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -56,8 +55,7 @@ class RoomListFiltersViewTests { rule.setContent { RoomListFiltersView( state = aRoomListFiltersState( - unselectedFilters = persistentListOf(), - selectedFilters = RoomListFilter.entries.toImmutableList(), + filterSelectionStates = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = true) }, eventSink = eventsRecorder ), ) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 0b4c47800c..e651e8f968 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -29,6 +29,7 @@ import io.element.android.features.roomlist.impl.datasource.DefaultInviteStateDa import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.filters.RoomListFiltersPresenter +import io.element.android.features.roomlist.impl.filters.selection.DefaultFilterSelectionStrategy import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.migration.SharedPrefsMigrationScreenStore import io.element.android.features.roomlist.impl.search.RoomListSearchDataSource @@ -127,6 +128,7 @@ class RoomListScreen( filtersPresenter = RoomListFiltersPresenter( roomListService = matrixClient.roomListService, featureFlagService = featureFlagService, + filterSelectionStrategy = DefaultFilterSelectionStrategy(), ), analyticsService = NoopAnalyticsService(), ) diff --git a/tests/testutils/build.gradle.kts b/tests/testutils/build.gradle.kts index 4b586043dc..8e985bf2ca 100644 --- a/tests/testutils/build.gradle.kts +++ b/tests/testutils/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(libs.test.junit) implementation(libs.test.truth) implementation(libs.coroutines.test) + implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.libraries.uiStrings) implementation(libs.test.turbine) diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/MutablePresenter.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/MutablePresenter.kt new file mode 100644 index 0000000000..02870f88c5 --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/MutablePresenter.kt @@ -0,0 +1,35 @@ +/* + * 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.tests.testutils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.flow.MutableStateFlow + +class MutablePresenter(initialState: State) : Presenter { + private val stateFlow = MutableStateFlow(initialState) + + fun updateState(state: State) { + stateFlow.value = state + } + + @Composable + override fun present(): State { + return stateFlow.collectAsState().value + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt index 0c8bd89951..abac27a14f 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -22,6 +22,7 @@ import app.cash.turbine.withTurbineTimeout import io.element.android.libraries.core.bool.orFalse import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds /** * Consume all items until timeout is reached waiting for an event or we receive terminal event. @@ -48,7 +49,7 @@ suspend fun ReceiveTurbine.awaitLastSequentialItem(): T { * @return the list of consumed items. */ suspend fun ReceiveTurbine.consumeItemsUntilPredicate( - timeout: Duration = 100.milliseconds, + timeout: Duration = 3.seconds, ignoreTimeoutError: Boolean = false, predicate: (T) -> Boolean, ): List { diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-7_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 71bb3edfb9..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-7_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f29850d64823d7243ab2b6ec3f885fae2cf88aa763b18f96735094ebef9f19cd -size 36421 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-8_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..bbaee0b3b3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Day-8_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d429c0ce8f4f34e7155775689bf8bc2efa61e83db6704e225592a2ea2c494b6 +size 48004 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-7_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2d2f436468..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-7_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f783da840d0b70462c9361cf5cd89f0c5636758925b99a4095ab82bab53fe10 -size 42000 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-8_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0d738df47f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_null_DefaultRoomListTopBarWithIndicator-Night-8_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:855f3544c544966b8ff7a4a83f02feb9326e9dc80dd792f6749575045fe6c951 +size 52543 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-6_7_null,NEXUS_5,1.0,en].png deleted file mode 100644 index ad7aac5334..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-6_7_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7b036518dc3f7d67d97a747f98a2909ff39461b4219a184200abc4e49d83e38b -size 36041 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-7_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5502bf853f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Day-7_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ec97a339d25fb7d0f928209567a81e13d65c2817ac21b372ff8573dc6d2f184 +size 47603 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-6_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2676f71941..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-6_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4743f2334377effb907c28b0bf0f550cd06f47f59ae6b71bf46de1299cf7c890 -size 41609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-7_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..14edd4016d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_DefaultRoomListTopBar_null_DefaultRoomListTopBar-Night-7_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad5dee1ca507cbc94f4ba21141afb75c074766dc7ef25eaa13ea7f296bcd6e32 +size 52194 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67eee6dc7a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9097d88f12cd44680cdcef752d45b12884217c65bac846d3007f16393b3d7233 +size 28036 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..14ed38eba4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f99dc73a1c501c8f3e2a61cce1f479b35cdf75fa3909a8a282f396d9ac93237 +size 24013 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8758ca3027 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01c07ac21d9ac4d9d916a4a15b27edc3d06f0f6206a736bc2ee5f0206c786523 +size 16006 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..016826429b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dd6178952670e12aa6f581793102afe44854457360bff654d7ca6089c8450ec +size 20112 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-11_12_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Day-6_7_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..752c60afa4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4e503d1c883b1b1b5a98f979afc04a2bc65aa23ba674ca3b96bcab3d7888773 +size 27993 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..67d9d5a9aa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30f05250641da8458b7891f881e6d50cd65f9e8d519672094df0ef7ed6c1afba +size 22795 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4cb9285f79 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ce4c893f2a0aa99384d16c531776604cbf2eab9040467f64b42a6e625fdb217 +size 15264 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..de981b0b85 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c2301c797291b295a3433e2d26e2ec485f9e3b0ea825dea8794bb06fe608c48 +size 19117 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-11_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-11_13_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomListContentView_null_RoomListContentView-Night-6_8_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Day-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Day-9_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Day-8_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Day-9_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Night-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Night-9_11_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Night-8_10_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryPlaceholderRow_null_RoomSummaryPlaceholderRow-Night-9_11_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_12,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_12,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_12,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_13,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_13,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_13,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_14,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_14,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_14,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_15,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_15,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_15,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_16,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_16,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_16,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_17,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_17,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_17,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_18,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_18,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_18,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_19,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_19,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_19,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_20,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_20,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_20,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_21,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_21,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_21,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_22,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_22,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_22,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_22,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_23,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_23,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_23,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_23,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_24,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_24,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_24,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_24,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_25,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_25,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_25,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_25,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_26,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_26,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_26,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_26,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_27,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_27,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_27,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_27,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-9_10_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-10_11_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_12,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_12,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_12,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_13,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_13,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_13,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_14,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_14,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_14,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_15,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_15,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_15,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_16,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_16,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_16,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_17,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_17,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_17,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_18,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_18,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_18,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_19,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_19,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_19,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_20,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_20,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_20,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_21,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_21,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_21,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_22,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_22,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_22,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_22,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_23,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_23,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_23,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_23,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_24,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_24,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_24,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_24,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_25,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_25,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_25,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_25,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_26,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_26,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_26,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_26,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_27,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_27,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_27,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_27,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-9_11_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-10_12_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 4df8ca69f4..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0bb5af2c6ad9a295f48dd1e7dbb0df838e55c294ba941658a34356bf1066bf88 -size 15056 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 38895fcaba..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-10_11_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:034ddb5e1a44b27f86f115741540e826b636d0130f44ea5afea41ada0cd42ea1 -size 14141 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b8292e1095 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a7ba4a60d167eaf29fb05343cd0c9fcc7c1790be840504e438a15812ee0fd47 +size 15780 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..20473f8f40 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Day-11_12_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13ea28681890a7ba8d8718782e6b29330de000d0643a5802be2468e8b8333a3d +size 14314 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index e8cdd870e8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8022de099e17b4e247f7d7f009425afeea7dc9ff3d8381cf0462392259e04968 -size 14943 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 8e5c5d0bd1..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-10_12_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:46aa240613c675c37f4152679c5103bdfc538238acc723952e311cd3a08666ae -size 13794 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..64c67a2a51 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02cad5768bb49b824bcf93bf2e527531d5f252cd241a2067cceb3dd66a8f2ea8 +size 14902 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2e7671de72 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.filters_RoomListFiltersView_null_RoomListFiltersView-Night-11_13_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55161cf8679ff87887a87e7fe39e03227e528709c0d189b37e26ed5b1f9ff888 +size 12961 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-12_13_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..99a9e03973 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Day-12_13_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c9d9aa75b2b01e9b0106377fbcd88a92fb8b4d6a34323b18264fed8f9a48cab +size 133347 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-12_14_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..46a349cbce --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.migration_MigrationView_null_MigrationView-Night-12_14_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fea824bf7f4c74c827fa3c1fa892b9beeb9d11e0863fc9e5c104c725236c7d10 +size 156506 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-12_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-13_14_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-12_13_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-13_14_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-12_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-13_14_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-12_13_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-13_14_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-12_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-13_15_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-12_14_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-13_15_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-12_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-13_15_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-12_14_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-13_15_null_1,NEXUS_5,1.0,en].png 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 67c12d1f9e..23df601373 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:ec79bcbd77e37a9fe6fa2c553571ec2665f5fee666926e25d96f365705a7ad26 -size 136977 +oid sha256:0151879112a55b3b310aaf0ed33a4409597572bd78063f6617e8869d95cb5362 +size 137048 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 index 0beb0e6949..19b6010d17 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3a65168c2219a33bcdde4644522adb1dad4606ae4e2843d5df47d271aabe24a -size 74218 +oid sha256:46e28da384551af27bf9c52a45c17b8441f8875494b3d3fd901ad66f5d7ff89b +size 74803 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 534122c6a6..6efd101dac 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:f2117daf834ffabc8ca24e738c26436b2a4c8bc02e478b928e5bc9f472ae2ef6 -size 54995 +oid sha256:a0037f0401b53088f2a75af67f8e4df374866fbce80c052535af1d97354c9b22 +size 56605 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 bafefb3d7a..106eadb403 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:aa3ed3df422a194420b24e6cd36bc3d822ff205f99ade1e2166f837b91553237 -size 160352 +oid sha256:a9723e58db10eb39c1b63a4c916e9d5d6c22de934945eec20703ca43f0f189f8 +size 159766 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 index 2a11b0fb05..52eccc7865 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5937fb0364f92fb2d5d6e2f4849d6725581903d0eff6c6c7c53b62430531eb4c -size 76907 +oid sha256:58ce650742396cd4addf9f545b2488bc2666e848b361a501d3ea2508c92c2a8a +size 76908 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 1654802e05..9b8391501a 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:b7522060e94ab9a3c221b58c314e8f4680ce1aef07c1820995dd0ece8a3c4ecf -size 57179 +oid sha256:528d43fa1989f1368c33f4f447347caa2fefb92c3a0a1e48846974ad96f262de +size 58510