From bda24a11752011cb05883299bfaf0f4db6bfcf69 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 Jun 2026 16:36:52 +0200 Subject: [PATCH 01/10] Forward message to multiple rooms --- .../roomselect/impl/RoomSelectEvents.kt | 5 +---- .../roomselect/impl/RoomSelectPresenter.kt | 17 +++++++-------- .../roomselect/impl/RoomSelectView.kt | 21 +++++++------------ .../impl/RoomSelectPresenterTest.kt | 4 ++-- 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt index a3512c419c..d0911b9b09 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt @@ -11,10 +11,7 @@ package io.element.android.libraries.roomselect.impl import io.element.android.libraries.matrix.ui.model.SelectRoomInfo sealed interface RoomSelectEvents { - data class SetSelectedRoom(val room: SelectRoomInfo) : RoomSelectEvents - - // TODO remove to restore multi-selection - data object RemoveSelectedRoom : RoomSelectEvents + data class ToggleSelectedRoom(val room: SelectRoomInfo) : RoomSelectEvents data object ToggleSearchActive : RoomSelectEvents data class UpdateVisibleRange(val range: IntRange) : RoomSelectEvents } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index b7547b8a45..ec1a60f0d3 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -69,17 +69,14 @@ class RoomSelectPresenter( fun handleEvent(event: RoomSelectEvents) { when (event) { - is RoomSelectEvents.SetSelectedRoom -> { - selectedRooms = persistentListOf(event.room) - // Restore for multi-selection -// val index = selectedRooms.indexOfFirst { it.roomId == event.room.roomId } -// selectedRooms = if (index >= 0) { -// selectedRooms.removeAt(index) -// } else { -// selectedRooms.add(event.room) -// } + is RoomSelectEvents.ToggleSelectedRoom -> { + val index = selectedRooms.indexOfFirst { it.roomId == event.room.roomId } + selectedRooms = if (index >= 0) { + selectedRooms.removeAt(index) + } else { + selectedRooms.add(event.room) + } } - RoomSelectEvents.RemoveSelectedRoom -> selectedRooms = persistentListOf() RoomSelectEvents.ToggleSearchActive -> isSearchActive = !isSearchActive is RoomSelectEvents.UpdateVisibleRange -> coroutineScope.launch { dataSource.updateVisibleRange(event.range) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 69f091ddc4..ee6f125e5a 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -71,15 +71,12 @@ fun RoomSelectView( onSubmit: (List) -> Unit, modifier: Modifier = Modifier, ) { - @Suppress("UNUSED_PARAMETER") fun onRoomRemoved(roomInfo: SelectRoomInfo) { - // TODO toggle selection when multi-selection is enabled - state.eventSink(RoomSelectEvents.RemoveSelectedRoom) + state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) } @Composable - fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList) { - if (isForwarding) return + fun SelectedRoomsHelper(selectedRooms: ImmutableList) { SelectedRooms( selectedRooms = selectedRooms, onRemoveRoom = ::onRoomRemoved, @@ -148,8 +145,6 @@ fun RoomSelectView( LazyColumn(state = lazyListState) { item { SelectedRoomsHelper( - // TODO state.isForwarding - isForwarding = false, selectedRooms = state.selectedRooms ) } @@ -159,7 +154,7 @@ fun RoomSelectView( roomSummary, isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> - state.eventSink(RoomSelectEvents.SetSelectedRoom(roomSummary)) + state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomSummary)) } ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) @@ -169,11 +164,9 @@ fun RoomSelectView( } if (!state.isSearchActive) { - // TODO restore for multi-selection -// SelectedRoomsHelper( -// isForwarding = state.isForwarding, -// selectedRooms = state.selectedRooms -// ) + SelectedRoomsHelper( + selectedRooms = state.selectedRooms + ) Spacer(modifier = Modifier.height(20.dp)) if (state.resultState is SearchBarResultState.Results) { @@ -184,7 +177,7 @@ fun RoomSelectView( roomSummary, isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> - state.eventSink(RoomSelectEvents.SetSelectedRoom(roomSummary)) + state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomSummary)) } ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt index 67c1b52231..f4024355f2 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt @@ -117,9 +117,9 @@ class RoomSelectPresenterTest { }.test { val initialState = awaitItem() val roomInfo = roomSummary.toSelectRoomInfo() - initialState.eventSink(RoomSelectEvents.SetSelectedRoom(roomInfo)) + initialState.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(roomInfo)) - initialState.eventSink(RoomSelectEvents.RemoveSelectedRoom) + initialState.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) assertThat(awaitItem().selectedRooms).isEmpty() cancel() } From 66c7c357852a0e2f50c4f6c444b86f3988b1c6dc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 Jun 2026 16:47:25 +0200 Subject: [PATCH 02/10] Ensure event is sent only once per room. --- .../android/libraries/matrix/impl/room/RoomContentForwarder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index e000c2f916..39cde68c31 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -49,7 +49,7 @@ class RoomContentForwarder( val content = (messageLikeContent.kind as? MsgLikeKind.Message)?.content ?: throw ForwardEventException(toRoomIds) - val targetRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } + val targetRooms = toRoomIds.toSet().mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } val failedForwardingTo = mutableSetOf() targetRooms.parallelMap { room -> room.use { targetRoom -> From 9739f58901238555702c6b2e3e7796b8cd715720 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 Jun 2026 16:49:57 +0200 Subject: [PATCH 03/10] `RoomSelectEvents` -> `RoomSelectEvent` --- .../impl/{RoomSelectEvents.kt => RoomSelectEvent.kt} | 8 ++++---- .../libraries/roomselect/impl/RoomSelectPresenter.kt | 8 ++++---- .../libraries/roomselect/impl/RoomSelectState.kt | 2 +- .../roomselect/impl/RoomSelectStateProvider.kt | 2 +- .../libraries/roomselect/impl/RoomSelectView.kt | 12 ++++++------ .../roomselect/impl/RoomSelectPresenterTest.kt | 12 ++++++------ 6 files changed, 22 insertions(+), 22 deletions(-) rename libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/{RoomSelectEvents.kt => RoomSelectEvent.kt} (81%) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvent.kt similarity index 81% rename from libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt rename to libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvent.kt index d0911b9b09..13c5bc5f8d 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvent.kt @@ -10,8 +10,8 @@ package io.element.android.libraries.roomselect.impl import io.element.android.libraries.matrix.ui.model.SelectRoomInfo -sealed interface RoomSelectEvents { - data class ToggleSelectedRoom(val room: SelectRoomInfo) : RoomSelectEvents - data object ToggleSearchActive : RoomSelectEvents - data class UpdateVisibleRange(val range: IntRange) : RoomSelectEvents +sealed interface RoomSelectEvent { + data class ToggleSelectedRoom(val room: SelectRoomInfo) : RoomSelectEvent + data object ToggleSearchActive : RoomSelectEvent + data class UpdateVisibleRange(val range: IntRange) : RoomSelectEvent } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index ec1a60f0d3..76488ea18e 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -67,9 +67,9 @@ class RoomSelectPresenter( } } - fun handleEvent(event: RoomSelectEvents) { + fun handleEvent(event: RoomSelectEvent) { when (event) { - is RoomSelectEvents.ToggleSelectedRoom -> { + is RoomSelectEvent.ToggleSelectedRoom -> { val index = selectedRooms.indexOfFirst { it.roomId == event.room.roomId } selectedRooms = if (index >= 0) { selectedRooms.removeAt(index) @@ -77,8 +77,8 @@ class RoomSelectPresenter( selectedRooms.add(event.room) } } - RoomSelectEvents.ToggleSearchActive -> isSearchActive = !isSearchActive - is RoomSelectEvents.UpdateVisibleRange -> coroutineScope.launch { + RoomSelectEvent.ToggleSearchActive -> isSearchActive = !isSearchActive + is RoomSelectEvent.UpdateVisibleRange -> coroutineScope.launch { dataSource.updateVisibleRange(event.range) } } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt index 927a1c02d6..d89dab7d72 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt @@ -20,5 +20,5 @@ data class RoomSelectState( val searchQuery: TextFieldState, val isSearchActive: Boolean, val selectedRooms: ImmutableList, - val eventSink: (RoomSelectEvents) -> Unit + val eventSink: (RoomSelectEvent) -> Unit ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt index b0d18659b6..1f8c10ec15 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt @@ -49,7 +49,7 @@ internal fun aRoomSelectState( searchQuery: String = "", isSearchActive: Boolean = false, selectedRooms: ImmutableList = persistentListOf(), - eventSink: (RoomSelectEvents) -> Unit = {}, + eventSink: (RoomSelectEvent) -> Unit = {}, ) = RoomSelectState( mode = mode, resultState = resultState, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index ee6f125e5a..0618430ba4 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -72,7 +72,7 @@ fun RoomSelectView( modifier: Modifier = Modifier, ) { fun onRoomRemoved(roomInfo: SelectRoomInfo) { - state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) + state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) } @Composable @@ -87,7 +87,7 @@ fun RoomSelectView( var canHandleBack by remember { mutableStateOf(true) } fun onBackButton(state: RoomSelectState) { if (state.isSearchActive) { - state.eventSink(RoomSelectEvents.ToggleSearchActive) + state.eventSink(RoomSelectEvent.ToggleSearchActive) } else if (canHandleBack) { canHandleBack = false onDismiss() @@ -101,7 +101,7 @@ fun RoomSelectView( val lazyListState = rememberLazyListState() OnVisibleRangeChangeEffect(lazyListState) { visibleRange -> - state.eventSink(RoomSelectEvents.UpdateVisibleRange(visibleRange)) + state.eventSink(RoomSelectEvent.UpdateVisibleRange(visibleRange)) } Scaffold( @@ -138,7 +138,7 @@ fun RoomSelectView( placeHolderTitle = stringResource(CommonStrings.action_search), queryState = state.searchQuery, active = state.isSearchActive, - onActiveChange = { state.eventSink(RoomSelectEvents.ToggleSearchActive) }, + onActiveChange = { state.eventSink(RoomSelectEvent.ToggleSearchActive) }, resultState = state.resultState, showBackButton = false, ) { summaries -> @@ -154,7 +154,7 @@ fun RoomSelectView( roomSummary, isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> - state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomSummary)) + state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary)) } ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) @@ -177,7 +177,7 @@ fun RoomSelectView( roomSummary, isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> - state.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomSummary)) + state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary)) } ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt index f4024355f2..720ec85ef0 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt @@ -57,9 +57,9 @@ class RoomSelectPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(RoomSelectEvents.ToggleSearchActive) + initialState.eventSink(RoomSelectEvent.ToggleSearchActive) assertThat(awaitItem().isSearchActive).isTrue() - initialState.eventSink(RoomSelectEvents.ToggleSearchActive) + initialState.eventSink(RoomSelectEvent.ToggleSearchActive) assertThat(awaitItem().isSearchActive).isFalse() } } @@ -84,7 +84,7 @@ class RoomSelectPresenterTest { // Do not compare the lambda because they will be different. So copy the lambda from expectedRoomSummary to result val result = (awaitItem().resultState as SearchBarResultState.Results).results assertThat(result).isEqualTo(listOf(expectedRoomInfo)) - initialState.eventSink(RoomSelectEvents.ToggleSearchActive) + initialState.eventSink(RoomSelectEvent.ToggleSearchActive) skipItems(1) initialState.searchQuery.setTextAndPlaceCursorAtEnd("string not contained") assertThat( @@ -117,9 +117,9 @@ class RoomSelectPresenterTest { }.test { val initialState = awaitItem() val roomInfo = roomSummary.toSelectRoomInfo() - initialState.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) + initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(roomInfo)) - initialState.eventSink(RoomSelectEvents.ToggleSelectedRoom(roomInfo)) + initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) assertThat(awaitItem().selectedRooms).isEmpty() cancel() } @@ -146,7 +146,7 @@ class RoomSelectPresenterTest { skipItems(1) // UpdateVisibleRange near end should trigger loadMore - initialState.eventSink(RoomSelectEvents.UpdateVisibleRange(IntRange(0, 9))) + initialState.eventSink(RoomSelectEvent.UpdateVisibleRange(IntRange(0, 9))) // Give time for the coroutine to complete testScheduler.advanceUntilIdle() From c0c2f59822e3e7c79ff1e3669061a9c59b498042 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 Jun 2026 16:50:53 +0200 Subject: [PATCH 04/10] Use test extension on presenter. --- .../impl/RoomSelectPresenterTest.kt | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt index 720ec85ef0..3542bddb32 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt @@ -9,9 +9,6 @@ package io.element.android.libraries.roomselect.impl import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd -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.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.roomlist.RoomListFilter @@ -24,6 +21,7 @@ import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope @@ -40,9 +38,7 @@ class RoomSelectPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createRoomSelectPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.selectedRooms).isEmpty() assertThat(initialState.resultState).isInstanceOf(SearchBarResultState.Initial::class.java) @@ -53,9 +49,7 @@ class RoomSelectPresenterTest { @Test fun `present - toggle search active`() = runTest { val presenter = createRoomSelectPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(RoomSelectEvent.ToggleSearchActive) assertThat(awaitItem().isSearchActive).isTrue() @@ -76,9 +70,7 @@ class RoomSelectPresenterTest { val presenter = createRoomSelectPresenter( roomListService = roomListService ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() val expectedRoomInfo = roomSummary.toSelectRoomInfo() // Do not compare the lambda because they will be different. So copy the lambda from expectedRoomSummary to result @@ -112,9 +104,7 @@ class RoomSelectPresenterTest { val presenter = createRoomSelectPresenter( roomListService = roomListService, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() val roomInfo = roomSummary.toSelectRoomInfo() initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) @@ -136,9 +126,7 @@ class RoomSelectPresenterTest { createRoomListLambda = { roomList } ) val presenter = createRoomSelectPresenter(roomListService = roomListService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() // Post some rooms to simulate loaded content val rooms = (1..10).map { aRoomSummary() } From d33852d32fe3aead2ad7bac21e351442c564c15c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 12 Jun 2026 09:22:29 +0200 Subject: [PATCH 05/10] Room selection: add a limitation of 10 rooms for Share and Forward actions. --- .../forward/impl/ForwardMessagesNode.kt | 5 ++++- .../android/features/share/impl/ShareNode.kt | 5 ++++- .../roomselect/api/RoomSelectEntryPoint.kt | 5 +++++ .../impl/DefaultRoomSelectEntryPoint.kt | 5 ++++- .../roomselect/impl/RoomSelectNode.kt | 6 +++++- .../roomselect/impl/RoomSelectPresenter.kt | 7 ++++++- .../roomselect/impl/RoomSelectState.kt | 7 +++++-- .../roomselect/impl/RoomSelectStateProvider.kt | 8 ++++++++ .../roomselect/impl/RoomSelectView.kt | 13 ++++++++++--- .../impl/DefaultRoomSelectEntryPointTest.kt | 13 +++++++++---- .../roomselect/impl/RoomSelectPresenterTest.kt | 18 +++++++++++++++--- 11 files changed, 75 insertions(+), 17 deletions(-) diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt index eb085d19c5..1c565a390a 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt @@ -74,7 +74,10 @@ class ForwardMessagesNode( return roomSelectEntryPoint.createNode( parentNode = this, buildContext = buildContext, - params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward), + params = RoomSelectEntryPoint.Params( + mode = RoomSelectMode.Forward, + maxNumberOfRooms = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS, + ), callback = callback, ) } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt index 0597b3b678..1abac1afa6 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt @@ -70,7 +70,10 @@ class ShareNode( return roomSelectEntryPoint.createNode( parentNode = this, buildContext = buildContext, - params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Share), + params = RoomSelectEntryPoint.Params( + mode = RoomSelectMode.Share, + maxNumberOfRooms = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS, + ), callback = callback, ) } diff --git a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt index ba5ca79d40..3c5c5d5ac3 100644 --- a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt +++ b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.core.RoomId interface RoomSelectEntryPoint : FeatureEntryPoint { data class Params( val mode: RoomSelectMode, + val maxNumberOfRooms: Int, ) fun createNode( @@ -30,4 +31,8 @@ interface RoomSelectEntryPoint : FeatureEntryPoint { fun onRoomSelected(roomIds: List) fun onCancel() } + + companion object { + const val DEFAULT_MAX_NUMBER_OF_ROOMS = 10 + } } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt index 4ca92663a9..ff3ba06f0d 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt @@ -26,7 +26,10 @@ class DefaultRoomSelectEntryPoint : RoomSelectEntryPoint { return parentNode.createNode( buildContext = buildContext, plugins = listOf( - RoomSelectNode.Inputs(mode = params.mode), + RoomSelectNode.Inputs( + mode = params.mode, + maxNumberOfRooms = params.maxNumberOfRooms, + ), callback, ) ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt index 943da643c6..0297f13066 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt @@ -35,10 +35,14 @@ class RoomSelectNode( ) : Node(buildContext, plugins = plugins) { data class Inputs( val mode: RoomSelectMode, + val maxNumberOfRooms: Int, ) : NodeInputs private val inputs: Inputs = inputs() - private val presenter = presenterFactory.create(inputs.mode) + private val presenter = presenterFactory.create( + mode = inputs.mode, + maxNumberOfRooms = inputs.maxNumberOfRooms, + ) private val stateFlow = launchMolecule { presenter.present() } private val callback: RoomSelectEntryPoint.Callback = callback() diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index 76488ea18e..e73299126e 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -34,11 +34,15 @@ import kotlinx.coroutines.launch @AssistedInject class RoomSelectPresenter( @Assisted private val mode: RoomSelectMode, + @Assisted private val maxNumberOfRooms: Int, private val dataSourceFactory: RoomSelectSearchDataSource.Factory, ) : Presenter { @AssistedFactory fun interface Factory { - fun create(mode: RoomSelectMode): RoomSelectPresenter + fun create( + mode: RoomSelectMode, + maxNumberOfRooms: Int, + ): RoomSelectPresenter } @Composable @@ -86,6 +90,7 @@ class RoomSelectPresenter( return RoomSelectState( mode = mode, + maxNumberOfRooms = maxNumberOfRooms, resultState = searchResults, searchQuery = queryState, isSearchActive = isSearchActive, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt index d89dab7d72..aadb7c10d1 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt @@ -16,9 +16,12 @@ import kotlinx.collections.immutable.ImmutableList data class RoomSelectState( val mode: RoomSelectMode, + val maxNumberOfRooms: Int, val resultState: SearchBarResultState>, val searchQuery: TextFieldState, val isSearchActive: Boolean, val selectedRooms: ImmutableList, - val eventSink: (RoomSelectEvent) -> Unit -) + val eventSink: (RoomSelectEvent) -> Unit, +) { + val canSelectMoreRooms = selectedRooms.size < maxNumberOfRooms +} diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt index 1f8c10ec15..131e35429e 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt @@ -40,11 +40,18 @@ open class RoomSelectStateProvider : PreviewParameterProvider { mode = RoomSelectMode.Share, resultState = SearchBarResultState.Results(aRoomSelectRoomList()), ), + aRoomSelectState( + mode = RoomSelectMode.Share, + resultState = SearchBarResultState.Results(aRoomSelectRoomList()), + selectedRooms = aRoomSelectRoomList().subList(0, 1), + maxNumberOfRooms = 1, + ), ) } internal fun aRoomSelectState( mode: RoomSelectMode = RoomSelectMode.Forward, + maxNumberOfRooms: Int = 10, resultState: SearchBarResultState> = SearchBarResultState.Initial(), searchQuery: String = "", isSearchActive: Boolean = false, @@ -52,6 +59,7 @@ internal fun aRoomSelectState( eventSink: (RoomSelectEvent) -> Unit = {}, ) = RoomSelectState( mode = mode, + maxNumberOfRooms = maxNumberOfRooms, resultState = resultState, searchQuery = TextFieldState(initialText = searchQuery), isSearchActive = isSearchActive, diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 0618430ba4..10d1212644 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -155,7 +155,8 @@ fun RoomSelectView( isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary)) - } + }, + canBeSelected = state.canSelectMoreRooms, ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) } @@ -178,7 +179,8 @@ fun RoomSelectView( isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId }, onSelection = { roomSummary -> state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary)) - } + }, + canBeSelected = state.canSelectMoreRooms, ) HorizontalDivider(modifier = Modifier.fillMaxWidth()) } @@ -211,6 +213,7 @@ private fun SelectedRooms( private fun RoomSummaryView( roomInfo: SelectRoomInfo, isSelected: Boolean, + canBeSelected: Boolean, onSelection: (SelectRoomInfo) -> Unit, ) { Row( @@ -255,7 +258,11 @@ private fun RoomSummaryView( ) } } - RadioButton(selected = isSelected, onClick = { onSelection(roomInfo) }) + RadioButton( + selected = isSelected, + enabled = isSelected || canBeSelected, + onClick = { onSelection(roomInfo) }, + ) } } diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt index a99a0396c2..ab9f0b2663 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt @@ -33,13 +33,15 @@ class DefaultRoomSelectEntryPointTest { fun `test node builder`() = runTest { val entryPoint = DefaultRoomSelectEntryPoint() val testMode = RoomSelectMode.Share + val testMaxNumberOfRooms = 12 val parentNode = TestParentNode.create { buildContext, plugins -> RoomSelectNode( buildContext = buildContext, plugins = plugins, - presenterFactory = { mode -> + presenterFactory = { mode, maxNumberOfRooms -> assertThat(mode).isEqualTo(testMode) - createRoomSelectPresenter(mode) + assertThat(maxNumberOfRooms).isEqualTo(testMaxNumberOfRooms) + createRoomSelectPresenter(mode, maxNumberOfRooms) }, ) } @@ -47,7 +49,10 @@ class DefaultRoomSelectEntryPointTest { override fun onRoomSelected(roomIds: List) = lambdaError() override fun onCancel() = lambdaError() } - val params = RoomSelectEntryPoint.Params(testMode) + val params = RoomSelectEntryPoint.Params( + mode = testMode, + maxNumberOfRooms = testMaxNumberOfRooms, + ) val result = entryPoint.createNode( parentNode = parentNode, buildContext = BuildContext.root(null), @@ -55,7 +60,7 @@ class DefaultRoomSelectEntryPointTest { callback = callback, ) assertThat(result).isInstanceOf(RoomSelectNode::class.java) - assertThat(result.plugins).contains(RoomSelectNode.Inputs(params.mode)) + assertThat(result.plugins).contains(RoomSelectNode.Inputs(params.mode, params.maxNumberOfRooms)) assertThat(result.plugins).contains(callback) } } diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt index 3542bddb32..b5a4ccfd12 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.roomlist.FakeDynamicRoomList import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.ui.model.toSelectRoomInfo +import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.assert @@ -43,6 +44,8 @@ class RoomSelectPresenterTest { assertThat(initialState.selectedRooms).isEmpty() assertThat(initialState.resultState).isInstanceOf(SearchBarResultState.Initial::class.java) assertThat(initialState.isSearchActive).isFalse() + assertThat(initialState.maxNumberOfRooms).isEqualTo(10) + assertThat(initialState.canSelectMoreRooms).isTrue() } } @@ -102,15 +105,22 @@ class RoomSelectPresenterTest { createRoomListLambda = { roomList } ) val presenter = createRoomSelectPresenter( + maxNumberOfRooms = 1, roomListService = roomListService, ) presenter.test { val initialState = awaitItem() val roomInfo = roomSummary.toSelectRoomInfo() initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) - assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(roomInfo)) - initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) - assertThat(awaitItem().selectedRooms).isEmpty() + awaitItem().let { + assertThat(it.selectedRooms).isEqualTo(persistentListOf(roomInfo)) + assertThat(it.canSelectMoreRooms).isFalse() + it.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo)) + } + awaitItem().let { + assertThat(it.selectedRooms).isEmpty() + assertThat(it.canSelectMoreRooms).isTrue() + } cancel() } } @@ -145,9 +155,11 @@ class RoomSelectPresenterTest { internal fun TestScope.createRoomSelectPresenter( mode: RoomSelectMode = RoomSelectMode.Forward, + maxNumberOfRooms: Int = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS, roomListService: RoomListService = FakeRoomListService(), ) = RoomSelectPresenter( mode = mode, + maxNumberOfRooms = maxNumberOfRooms, dataSourceFactory = object : RoomSelectSearchDataSource.Factory { override fun create(coroutineScope: CoroutineScope): RoomSelectSearchDataSource { return RoomSelectSearchDataSource( From 2fd4e6857a32747d847f4cd97c434e50f2effa3e Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 12 Jun 2026 07:38:25 +0000 Subject: [PATCH 06/10] Update screenshots --- .../libraries.roomselect.impl_RoomSelectView_Day_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_6_en.png | 3 +++ .../libraries.roomselect.impl_RoomSelectView_Night_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_6_en.png | 3 +++ 6 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png index 051db89ece..3e6b5db08b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d06e84c695086a799c8377bcf48869675b98dcadd1b5320e685d775589ac40c7 -size 30950 +oid sha256:05b23d318c4dd55d75dccd2bd9ad97f8dbc255a53c77562afc95ccad0ec2787f +size 31066 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png index c19c6f4fbb..c8b121c482 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cccff3215bc81e2a6491f3b0da3f4d8997179b54c698f962eb245f535e6cc9fc -size 28655 +oid sha256:cb1875e976dbbce262646028234a1886ce9188638543d858993d2c718c1feb68 +size 28803 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png new file mode 100644 index 0000000000..7650575279 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31e4bad98daa06ba608a8557afbf4ee5106cc92f91a402b279aac764ef0a43d3 +size 32767 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png index 97ff3939e2..db9969ec26 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b38450dc7cc24e6f9a0db829aa11058d10ae45d433945afd534627534f362554 -size 30261 +oid sha256:cf6ff4a45ba852a4fba0208f0cf2a86545206617045c964c3ca53504f87ba9a8 +size 30347 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png index 5301142de1..ba68546048 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12350783e27c09074bdaba2e74ac445b643ff6a13251c13a7cdf98999274325d -size 27915 +oid sha256:e9edd40989e3457845cf26677bab7d0a2cdb46e007a41888357a0ea8a516796f +size 28017 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png new file mode 100644 index 0000000000..64bb72be26 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:792cf6409658fa1af7b0f3637b89d4d6c4a76d5975959ab045c4028e5d62ce9f +size 32026 From a819cf6862170578a57d737122665fae450b1e04 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 12 Jun 2026 12:30:19 +0200 Subject: [PATCH 07/10] Fix padding issue --- .../roomselect/impl/RoomSelectView.kt | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 10d1212644..7e917b1368 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -76,12 +76,19 @@ fun RoomSelectView( } @Composable - fun SelectedRoomsHelper(selectedRooms: ImmutableList) { - SelectedRooms( - selectedRooms = selectedRooms, - onRemoveRoom = ::onRoomRemoved, - modifier = Modifier.padding(vertical = 16.dp) - ) + fun SelectedRoomsHelper( + selectedRooms: ImmutableList, + showVerticalSpace: Boolean, + ) { + if (selectedRooms.isNotEmpty()) { + SelectedRooms( + selectedRooms = selectedRooms, + onRemoveRoom = ::onRoomRemoved, + modifier = Modifier.padding(vertical = 16.dp) + ) + } else if (showVerticalSpace) { + Spacer(modifier = Modifier.height(20.dp)) + } } var canHandleBack by remember { mutableStateOf(true) } @@ -145,7 +152,8 @@ fun RoomSelectView( LazyColumn(state = lazyListState) { item { SelectedRoomsHelper( - selectedRooms = state.selectedRooms + selectedRooms = state.selectedRooms, + showVerticalSpace = false, ) } items(summaries, key = { it.roomId.value }) { roomSummary -> @@ -166,10 +174,9 @@ fun RoomSelectView( if (!state.isSearchActive) { SelectedRoomsHelper( - selectedRooms = state.selectedRooms + selectedRooms = state.selectedRooms, + showVerticalSpace = true, ) - Spacer(modifier = Modifier.height(20.dp)) - if (state.resultState is SearchBarResultState.Results) { LazyColumn(state = lazyListState) { items(state.resultState.results, key = { it.roomId.value }) { roomSummary -> From 4ed247eb9d9d3c5f9cdfce95e01ee301d722e611 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 12 Jun 2026 10:45:34 +0000 Subject: [PATCH 08/10] Update screenshots --- .../libraries.roomselect.impl_RoomSelectView_Day_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_6_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_6_en.png | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png index 3e6b5db08b..051db89ece 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05b23d318c4dd55d75dccd2bd9ad97f8dbc255a53c77562afc95ccad0ec2787f -size 31066 +oid sha256:d06e84c695086a799c8377bcf48869675b98dcadd1b5320e685d775589ac40c7 +size 30950 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png index ddde486abc..2248741c9e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3825e3c312d75a865545745ee00b64d8c2259e515dc45fad64236963e91f2427 -size 28999 +oid sha256:75613191bea4b62a86510e3ae323ce5a46553da13f10825bc3769f1daf48dc97 +size 28903 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png index c8b121c482..c19c6f4fbb 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb1875e976dbbce262646028234a1886ce9188638543d858993d2c718c1feb68 -size 28803 +oid sha256:cccff3215bc81e2a6491f3b0da3f4d8997179b54c698f962eb245f535e6cc9fc +size 28655 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png index 7650575279..bc3c42a68b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31e4bad98daa06ba608a8557afbf4ee5106cc92f91a402b279aac764ef0a43d3 -size 32767 +oid sha256:4311e7f7371cb25c7c7fec50c92007d521e7b71b8398a8e2d756243b568f69da +size 32887 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png index db9969ec26..97ff3939e2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf6ff4a45ba852a4fba0208f0cf2a86545206617045c964c3ca53504f87ba9a8 -size 30347 +oid sha256:b38450dc7cc24e6f9a0db829aa11058d10ae45d433945afd534627534f362554 +size 30261 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png index 1c3dfb9480..a46a269a25 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1b36608e269c7572c93c6f10cddeaf42bfeba404c6699a6e050144bc28bc55e -size 28569 +oid sha256:b3d00c887b692dd572f0f5a09695ddd9dbff907c2a9d1c3062d62f5ddafdd816 +size 28479 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png index ba68546048..5301142de1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9edd40989e3457845cf26677bab7d0a2cdb46e007a41888357a0ea8a516796f -size 28017 +oid sha256:12350783e27c09074bdaba2e74ac445b643ff6a13251c13a7cdf98999274325d +size 27915 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png index 64bb72be26..d5990ac1b0 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:792cf6409658fa1af7b0f3637b89d4d6c4a76d5975959ab045c4028e5d62ce9f -size 32026 +oid sha256:d1d6bc87e8be8a846ba9ebe5b909e1968ad0e087f81dd93209b19c61c54b0219 +size 32021 From f7182127a94cae31a1bacdd16ae0cf6e5ac54a02 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 17 Jun 2026 10:41:47 +0200 Subject: [PATCH 09/10] `RadioButton` -> `Checkbox` --- .../android/libraries/roomselect/impl/RoomSelectView.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 7e917b1368..508fcd4fc2 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -44,8 +44,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Checkbox import io.element.android.libraries.designsystem.theme.components.HorizontalDivider -import io.element.android.libraries.designsystem.theme.components.RadioButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.SearchBar import io.element.android.libraries.designsystem.theme.components.SearchBarResultState @@ -265,10 +265,10 @@ private fun RoomSummaryView( ) } } - RadioButton( - selected = isSelected, + Checkbox( + checked = isSelected, enabled = isSelected || canBeSelected, - onClick = { onSelection(roomInfo) }, + onCheckedChange = { onSelection(roomInfo) }, ) } } From e4098c2e00f3b29637c64b2649da48d8f076223a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 17 Jun 2026 08:57:31 +0000 Subject: [PATCH 10/10] Update screenshots --- .../libraries.roomselect.impl_RoomSelectView_Day_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_4_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_6_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_4_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_5_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_6_en.png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png index 051db89ece..3ae01ed93b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d06e84c695086a799c8377bcf48869675b98dcadd1b5320e685d775589ac40c7 -size 30950 +oid sha256:2a93b747f763fc0613c3fa5fdb492be034426916a4b3761a61f4c1d5fc655440 +size 28772 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png index 2248741c9e..683ee8e120 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75613191bea4b62a86510e3ae323ce5a46553da13f10825bc3769f1daf48dc97 -size 28903 +oid sha256:feaf2f316024a21d49c7b71b1e3df892e60924a715fc892871ee1f06006145ab +size 26657 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png index 821a37d7c0..817e4d44d2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b0f7e2b97a343d0e1bedd9595a1057a03a4429c3fa72613451ac86a7146392a -size 33382 +oid sha256:19f984bbf1f12dc3774b24c4fbadaae40c024cc876c8bf172af29a22429b43e0 +size 31248 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png index c19c6f4fbb..4dc0de9f33 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cccff3215bc81e2a6491f3b0da3f4d8997179b54c698f962eb245f535e6cc9fc -size 28655 +oid sha256:c3ff0ef8dc9adc1378bef8a87479aed5a4cc1d43050851f516e9de17ef7201ee +size 26450 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png index bc3c42a68b..c9f8f4a0ea 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4311e7f7371cb25c7c7fec50c92007d521e7b71b8398a8e2d756243b568f69da -size 32887 +oid sha256:b1dd54a38725d9fdc035a73aebfbe9447377970e1b9e33dd512f0d41e2a11ec1 +size 30975 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png index 97ff3939e2..67d226b286 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b38450dc7cc24e6f9a0db829aa11058d10ae45d433945afd534627534f362554 -size 30261 +oid sha256:c3545c6c2b173b70bfba5dd571ea23db31ca7c2e64554131ba5cdbbb0555ea35 +size 28229 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png index a46a269a25..8b6772b858 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3d00c887b692dd572f0f5a09695ddd9dbff907c2a9d1c3062d62f5ddafdd816 -size 28479 +oid sha256:947bb1138d11180f6174db8b9fadf51cec8d8d2947f0f47db148b479fa1f4d86 +size 26340 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png index c4689af159..5d252a9af5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96f198461d8d60d8610f337c3dbe90bebd8c98f32a4739310532d629ca336a1f -size 33113 +oid sha256:e035e0c89cf000340d17257a9ad0e3e2ea1937f7408664742d9c4ba0c5b8c2c3 +size 31149 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png index 5301142de1..4c905ad8d9 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12350783e27c09074bdaba2e74ac445b643ff6a13251c13a7cdf98999274325d -size 27915 +oid sha256:f0159ba2e298d8c3c7663ed0fdb4dca4ae179e00d40e5fdec8223ca597adbe6a +size 25871 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png index d5990ac1b0..42d6e7a45f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1d6bc87e8be8a846ba9ebe5b909e1968ad0e087f81dd93209b19c61c54b0219 -size 32021 +oid sha256:42ff2caf57dc21ea2681423082d91592091ad062ba350e91a7bbd10500768d58 +size 30515