mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-04 10:32:34 +00:00
Room selection: add a limitation of 10 rooms for Share and Forward actions.
This commit is contained in:
+4
-1
@@ -74,7 +74,10 @@ class ForwardMessagesNode(
|
|||||||
return roomSelectEntryPoint.createNode(
|
return roomSelectEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward),
|
params = RoomSelectEntryPoint.Params(
|
||||||
|
mode = RoomSelectMode.Forward,
|
||||||
|
maxNumberOfRooms = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS,
|
||||||
|
),
|
||||||
callback = callback,
|
callback = callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -70,7 +70,10 @@ class ShareNode(
|
|||||||
return roomSelectEntryPoint.createNode(
|
return roomSelectEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Share),
|
params = RoomSelectEntryPoint.Params(
|
||||||
|
mode = RoomSelectMode.Share,
|
||||||
|
maxNumberOfRooms = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS,
|
||||||
|
),
|
||||||
callback = callback,
|
callback = callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||||||
interface RoomSelectEntryPoint : FeatureEntryPoint {
|
interface RoomSelectEntryPoint : FeatureEntryPoint {
|
||||||
data class Params(
|
data class Params(
|
||||||
val mode: RoomSelectMode,
|
val mode: RoomSelectMode,
|
||||||
|
val maxNumberOfRooms: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun createNode(
|
fun createNode(
|
||||||
@@ -30,4 +31,8 @@ interface RoomSelectEntryPoint : FeatureEntryPoint {
|
|||||||
fun onRoomSelected(roomIds: List<RoomId>)
|
fun onRoomSelected(roomIds: List<RoomId>)
|
||||||
fun onCancel()
|
fun onCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DEFAULT_MAX_NUMBER_OF_ROOMS = 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -26,7 +26,10 @@ class DefaultRoomSelectEntryPoint : RoomSelectEntryPoint {
|
|||||||
return parentNode.createNode<RoomSelectNode>(
|
return parentNode.createNode<RoomSelectNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(
|
plugins = listOf(
|
||||||
RoomSelectNode.Inputs(mode = params.mode),
|
RoomSelectNode.Inputs(
|
||||||
|
mode = params.mode,
|
||||||
|
maxNumberOfRooms = params.maxNumberOfRooms,
|
||||||
|
),
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-1
@@ -35,10 +35,14 @@ class RoomSelectNode(
|
|||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
val mode: RoomSelectMode,
|
val mode: RoomSelectMode,
|
||||||
|
val maxNumberOfRooms: Int,
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
private val inputs: Inputs = inputs()
|
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 stateFlow = launchMolecule { presenter.present() }
|
||||||
private val callback: RoomSelectEntryPoint.Callback = callback()
|
private val callback: RoomSelectEntryPoint.Callback = callback()
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -34,11 +34,15 @@ import kotlinx.coroutines.launch
|
|||||||
@AssistedInject
|
@AssistedInject
|
||||||
class RoomSelectPresenter(
|
class RoomSelectPresenter(
|
||||||
@Assisted private val mode: RoomSelectMode,
|
@Assisted private val mode: RoomSelectMode,
|
||||||
|
@Assisted private val maxNumberOfRooms: Int,
|
||||||
private val dataSourceFactory: RoomSelectSearchDataSource.Factory,
|
private val dataSourceFactory: RoomSelectSearchDataSource.Factory,
|
||||||
) : Presenter<RoomSelectState> {
|
) : Presenter<RoomSelectState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
fun create(mode: RoomSelectMode): RoomSelectPresenter
|
fun create(
|
||||||
|
mode: RoomSelectMode,
|
||||||
|
maxNumberOfRooms: Int,
|
||||||
|
): RoomSelectPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -86,6 +90,7 @@ class RoomSelectPresenter(
|
|||||||
|
|
||||||
return RoomSelectState(
|
return RoomSelectState(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
|
maxNumberOfRooms = maxNumberOfRooms,
|
||||||
resultState = searchResults,
|
resultState = searchResults,
|
||||||
searchQuery = queryState,
|
searchQuery = queryState,
|
||||||
isSearchActive = isSearchActive,
|
isSearchActive = isSearchActive,
|
||||||
|
|||||||
+5
-2
@@ -16,9 +16,12 @@ import kotlinx.collections.immutable.ImmutableList
|
|||||||
|
|
||||||
data class RoomSelectState(
|
data class RoomSelectState(
|
||||||
val mode: RoomSelectMode,
|
val mode: RoomSelectMode,
|
||||||
|
val maxNumberOfRooms: Int,
|
||||||
val resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>>,
|
val resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>>,
|
||||||
val searchQuery: TextFieldState,
|
val searchQuery: TextFieldState,
|
||||||
val isSearchActive: Boolean,
|
val isSearchActive: Boolean,
|
||||||
val selectedRooms: ImmutableList<SelectRoomInfo>,
|
val selectedRooms: ImmutableList<SelectRoomInfo>,
|
||||||
val eventSink: (RoomSelectEvent) -> Unit
|
val eventSink: (RoomSelectEvent) -> Unit,
|
||||||
)
|
) {
|
||||||
|
val canSelectMoreRooms = selectedRooms.size < maxNumberOfRooms
|
||||||
|
}
|
||||||
|
|||||||
+8
@@ -40,11 +40,18 @@ open class RoomSelectStateProvider : PreviewParameterProvider<RoomSelectState> {
|
|||||||
mode = RoomSelectMode.Share,
|
mode = RoomSelectMode.Share,
|
||||||
resultState = SearchBarResultState.Results(aRoomSelectRoomList()),
|
resultState = SearchBarResultState.Results(aRoomSelectRoomList()),
|
||||||
),
|
),
|
||||||
|
aRoomSelectState(
|
||||||
|
mode = RoomSelectMode.Share,
|
||||||
|
resultState = SearchBarResultState.Results(aRoomSelectRoomList()),
|
||||||
|
selectedRooms = aRoomSelectRoomList().subList(0, 1),
|
||||||
|
maxNumberOfRooms = 1,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aRoomSelectState(
|
internal fun aRoomSelectState(
|
||||||
mode: RoomSelectMode = RoomSelectMode.Forward,
|
mode: RoomSelectMode = RoomSelectMode.Forward,
|
||||||
|
maxNumberOfRooms: Int = 10,
|
||||||
resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>> = SearchBarResultState.Initial(),
|
resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>> = SearchBarResultState.Initial(),
|
||||||
searchQuery: String = "",
|
searchQuery: String = "",
|
||||||
isSearchActive: Boolean = false,
|
isSearchActive: Boolean = false,
|
||||||
@@ -52,6 +59,7 @@ internal fun aRoomSelectState(
|
|||||||
eventSink: (RoomSelectEvent) -> Unit = {},
|
eventSink: (RoomSelectEvent) -> Unit = {},
|
||||||
) = RoomSelectState(
|
) = RoomSelectState(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
|
maxNumberOfRooms = maxNumberOfRooms,
|
||||||
resultState = resultState,
|
resultState = resultState,
|
||||||
searchQuery = TextFieldState(initialText = searchQuery),
|
searchQuery = TextFieldState(initialText = searchQuery),
|
||||||
isSearchActive = isSearchActive,
|
isSearchActive = isSearchActive,
|
||||||
|
|||||||
+10
-3
@@ -155,7 +155,8 @@ fun RoomSelectView(
|
|||||||
isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId },
|
isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId },
|
||||||
onSelection = { roomSummary ->
|
onSelection = { roomSummary ->
|
||||||
state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary))
|
state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary))
|
||||||
}
|
},
|
||||||
|
canBeSelected = state.canSelectMoreRooms,
|
||||||
)
|
)
|
||||||
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
||||||
}
|
}
|
||||||
@@ -178,7 +179,8 @@ fun RoomSelectView(
|
|||||||
isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId },
|
isSelected = state.selectedRooms.any { it.roomId == roomSummary.roomId },
|
||||||
onSelection = { roomSummary ->
|
onSelection = { roomSummary ->
|
||||||
state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary))
|
state.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomSummary))
|
||||||
}
|
},
|
||||||
|
canBeSelected = state.canSelectMoreRooms,
|
||||||
)
|
)
|
||||||
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
||||||
}
|
}
|
||||||
@@ -211,6 +213,7 @@ private fun SelectedRooms(
|
|||||||
private fun RoomSummaryView(
|
private fun RoomSummaryView(
|
||||||
roomInfo: SelectRoomInfo,
|
roomInfo: SelectRoomInfo,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
|
canBeSelected: Boolean,
|
||||||
onSelection: (SelectRoomInfo) -> Unit,
|
onSelection: (SelectRoomInfo) -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -255,7 +258,11 @@ private fun RoomSummaryView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RadioButton(selected = isSelected, onClick = { onSelection(roomInfo) })
|
RadioButton(
|
||||||
|
selected = isSelected,
|
||||||
|
enabled = isSelected || canBeSelected,
|
||||||
|
onClick = { onSelection(roomInfo) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -33,13 +33,15 @@ class DefaultRoomSelectEntryPointTest {
|
|||||||
fun `test node builder`() = runTest {
|
fun `test node builder`() = runTest {
|
||||||
val entryPoint = DefaultRoomSelectEntryPoint()
|
val entryPoint = DefaultRoomSelectEntryPoint()
|
||||||
val testMode = RoomSelectMode.Share
|
val testMode = RoomSelectMode.Share
|
||||||
|
val testMaxNumberOfRooms = 12
|
||||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||||
RoomSelectNode(
|
RoomSelectNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = { mode ->
|
presenterFactory = { mode, maxNumberOfRooms ->
|
||||||
assertThat(mode).isEqualTo(testMode)
|
assertThat(mode).isEqualTo(testMode)
|
||||||
createRoomSelectPresenter(mode)
|
assertThat(maxNumberOfRooms).isEqualTo(testMaxNumberOfRooms)
|
||||||
|
createRoomSelectPresenter(mode, maxNumberOfRooms)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -47,7 +49,10 @@ class DefaultRoomSelectEntryPointTest {
|
|||||||
override fun onRoomSelected(roomIds: List<RoomId>) = lambdaError()
|
override fun onRoomSelected(roomIds: List<RoomId>) = lambdaError()
|
||||||
override fun onCancel() = lambdaError()
|
override fun onCancel() = lambdaError()
|
||||||
}
|
}
|
||||||
val params = RoomSelectEntryPoint.Params(testMode)
|
val params = RoomSelectEntryPoint.Params(
|
||||||
|
mode = testMode,
|
||||||
|
maxNumberOfRooms = testMaxNumberOfRooms,
|
||||||
|
)
|
||||||
val result = entryPoint.createNode(
|
val result = entryPoint.createNode(
|
||||||
parentNode = parentNode,
|
parentNode = parentNode,
|
||||||
buildContext = BuildContext.root(null),
|
buildContext = BuildContext.root(null),
|
||||||
@@ -55,7 +60,7 @@ class DefaultRoomSelectEntryPointTest {
|
|||||||
callback = callback,
|
callback = callback,
|
||||||
)
|
)
|
||||||
assertThat(result).isInstanceOf(RoomSelectNode::class.java)
|
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)
|
assertThat(result.plugins).contains(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-3
@@ -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.FakeDynamicRoomList
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.libraries.matrix.ui.model.toSelectRoomInfo
|
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.libraries.roomselect.api.RoomSelectMode
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.assert
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
@@ -43,6 +44,8 @@ class RoomSelectPresenterTest {
|
|||||||
assertThat(initialState.selectedRooms).isEmpty()
|
assertThat(initialState.selectedRooms).isEmpty()
|
||||||
assertThat(initialState.resultState).isInstanceOf(SearchBarResultState.Initial::class.java)
|
assertThat(initialState.resultState).isInstanceOf(SearchBarResultState.Initial::class.java)
|
||||||
assertThat(initialState.isSearchActive).isFalse()
|
assertThat(initialState.isSearchActive).isFalse()
|
||||||
|
assertThat(initialState.maxNumberOfRooms).isEqualTo(10)
|
||||||
|
assertThat(initialState.canSelectMoreRooms).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,15 +105,22 @@ class RoomSelectPresenterTest {
|
|||||||
createRoomListLambda = { roomList }
|
createRoomListLambda = { roomList }
|
||||||
)
|
)
|
||||||
val presenter = createRoomSelectPresenter(
|
val presenter = createRoomSelectPresenter(
|
||||||
|
maxNumberOfRooms = 1,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
)
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
val roomInfo = roomSummary.toSelectRoomInfo()
|
val roomInfo = roomSummary.toSelectRoomInfo()
|
||||||
initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo))
|
initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo))
|
||||||
assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(roomInfo))
|
awaitItem().let {
|
||||||
initialState.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo))
|
assertThat(it.selectedRooms).isEqualTo(persistentListOf(roomInfo))
|
||||||
assertThat(awaitItem().selectedRooms).isEmpty()
|
assertThat(it.canSelectMoreRooms).isFalse()
|
||||||
|
it.eventSink(RoomSelectEvent.ToggleSelectedRoom(roomInfo))
|
||||||
|
}
|
||||||
|
awaitItem().let {
|
||||||
|
assertThat(it.selectedRooms).isEmpty()
|
||||||
|
assertThat(it.canSelectMoreRooms).isTrue()
|
||||||
|
}
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,9 +155,11 @@ class RoomSelectPresenterTest {
|
|||||||
|
|
||||||
internal fun TestScope.createRoomSelectPresenter(
|
internal fun TestScope.createRoomSelectPresenter(
|
||||||
mode: RoomSelectMode = RoomSelectMode.Forward,
|
mode: RoomSelectMode = RoomSelectMode.Forward,
|
||||||
|
maxNumberOfRooms: Int = RoomSelectEntryPoint.DEFAULT_MAX_NUMBER_OF_ROOMS,
|
||||||
roomListService: RoomListService = FakeRoomListService(),
|
roomListService: RoomListService = FakeRoomListService(),
|
||||||
) = RoomSelectPresenter(
|
) = RoomSelectPresenter(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
|
maxNumberOfRooms = maxNumberOfRooms,
|
||||||
dataSourceFactory = object : RoomSelectSearchDataSource.Factory {
|
dataSourceFactory = object : RoomSelectSearchDataSource.Factory {
|
||||||
override fun create(coroutineScope: CoroutineScope): RoomSelectSearchDataSource {
|
override fun create(coroutineScope: CoroutineScope): RoomSelectSearchDataSource {
|
||||||
return RoomSelectSearchDataSource(
|
return RoomSelectSearchDataSource(
|
||||||
|
|||||||
Reference in New Issue
Block a user