Browse Source

Adding multi/single selection variants

test/jme/compound-poc
Maxime NATUREL 2 years ago committed by Florian Renaud
parent
commit
f536c6308d
  1. 6
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt
  2. 13
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt
  3. 15
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt
  4. 21
      features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt
  5. 2
      features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt
  6. 1
      features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt
  7. 52
      features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt
  8. 140
      features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt
  9. 33
      features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt
  10. 32
      features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt
  11. 15
      features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt
  12. 6
      features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt

6
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt

@ -17,19 +17,19 @@
package io.element.android.features.createroom.impl.root package io.element.android.features.createroom.impl.root
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.features.selectusers.api.SelectSingleUserPresenter
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class CreateRoomRootPresenter @Inject constructor( class CreateRoomRootPresenter @Inject constructor(
private val selectUsersPresenter: SelectUsersPresenter, private val selectSingleUserPresenter: SelectSingleUserPresenter,
) : Presenter<CreateRoomRootState> { ) : Presenter<CreateRoomRootState> {
@Composable @Composable
override fun present(): CreateRoomRootState { override fun present(): CreateRoomRootState {
val selectUsersState = selectUsersPresenter.present() val selectUsersState = selectSingleUserPresenter.present()
fun handleEvents(event: CreateRoomRootEvents) { fun handleEvents(event: CreateRoomRootEvents) {
when (event) { when (event) {

13
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt

@ -17,10 +17,7 @@
package io.element.android.features.createroom.impl.root package io.element.android.features.createroom.impl.root
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.selectusers.api.SelectUsersState import io.element.android.features.selectusers.api.aSelectUsersState
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.persistentListOf
open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRootState> { open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRootState> {
override val values: Sequence<CreateRoomRootState> override val values: Sequence<CreateRoomRootState>
@ -31,11 +28,5 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
fun aCreateRoomRootState() = CreateRoomRootState( fun aCreateRoomRootState() = CreateRoomRootState(
eventSink = {}, eventSink = {},
selectUsersState = SelectUsersState( selectUsersState = aSelectUsersState(),
searchQuery = "",
searchResults = persistentListOf(),
selectedUsers = persistentListOf(),
isSearchActive = false,
eventSink = {},
)
) )

15
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt

@ -24,40 +24,27 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import io.element.android.features.selectusers.api.SearchUserBar
import io.element.android.features.selectusers.api.SelectUsersView import io.element.android.features.selectusers.api.SelectUsersView
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
import io.element.android.libraries.designsystem.theme.components.Icon 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.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold 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.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import io.element.android.libraries.designsystem.R as DrawableR import io.element.android.libraries.designsystem.R as DrawableR
import io.element.android.libraries.ui.strings.R as StringR import io.element.android.libraries.ui.strings.R as StringR
@ -83,7 +70,7 @@ fun CreateRoomRootView(
) { ) {
SelectUsersView( SelectUsersView(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
state = state.selectUsersState state = state.selectUsersState,
) )
if (!state.selectUsersState.isSearchActive) { if (!state.selectUsersState.isSearchActive) {

21
features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt

@ -0,0 +1,21 @@
/*
* 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.selectusers.api
import io.element.android.libraries.architecture.Presenter
interface SelectMultipleUsersPresenter : Presenter<SelectUsersState>

2
features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt → features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt

@ -18,4 +18,4 @@ package io.element.android.features.selectusers.api
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
interface SelectUsersPresenter : Presenter<SelectUsersState> interface SelectSingleUserPresenter : Presenter<SelectUsersState>

1
features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt

@ -24,5 +24,6 @@ data class SelectUsersState(
val searchResults: ImmutableList<MatrixUser>, val searchResults: ImmutableList<MatrixUser>,
val selectedUsers: ImmutableList<MatrixUser>, val selectedUsers: ImmutableList<MatrixUser>,
val isSearchActive: Boolean, val isSearchActive: Boolean,
val isMultiSelectionEnabled: Boolean,
val eventSink: (SelectUsersEvents) -> Unit, val eventSink: (SelectUsersEvents) -> Unit,
) )

52
features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt

@ -24,31 +24,23 @@ import kotlinx.collections.immutable.persistentListOf
open class SelectUsersStateProvider : PreviewParameterProvider<SelectUsersState> { open class SelectUsersStateProvider : PreviewParameterProvider<SelectUsersState> {
override val values: Sequence<SelectUsersState> override val values: Sequence<SelectUsersState>
get() = sequenceOf( get() = sequenceOf(
// TODO add states with selectedUsers
aSelectUsersState(), aSelectUsersState(),
aSelectUsersState().copy(isSearchActive = true), aSelectUsersState().copy(isSearchActive = true),
aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"), aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"),
aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone", isMultiSelectionEnabled = true),
aSelectUsersState().copy( aSelectUsersState().copy(
isSearchActive = true, isSearchActive = true,
searchQuery = "@someone:matrix.org", searchQuery = "@someone:matrix.org",
searchResults = persistentListOf( selectedUsers = aListOfSelectedUsers(),
MatrixUser(id = UserId("@someone:matrix.org")), searchResults = aListOfResults(),
MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"),
MatrixUser(
id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"),
username = "hey, I am someone with a very long display name"
),
MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"),
MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"),
MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"),
MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"),
MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"),
MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"),
MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"),
MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"),
MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"),
)
), ),
aSelectUsersState().copy(
isSearchActive = true,
searchQuery = "@someone:matrix.org",
isMultiSelectionEnabled = true,
selectedUsers = aListOfSelectedUsers(),
searchResults = aListOfResults(),
)
) )
} }
@ -57,5 +49,29 @@ fun aSelectUsersState() = SelectUsersState(
searchQuery = "", searchQuery = "",
searchResults = persistentListOf(), searchResults = persistentListOf(),
selectedUsers = persistentListOf(), selectedUsers = persistentListOf(),
isMultiSelectionEnabled = false,
eventSink = {} eventSink = {}
) )
fun aListOfSelectedUsers() = persistentListOf(
MatrixUser(id = UserId("@someone:matrix.org")),
MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"),
)
fun aListOfResults() = persistentListOf(
MatrixUser(id = UserId("@someone:matrix.org")),
MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"),
MatrixUser(
id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"),
username = "hey, I am someone with a very long display name"
),
MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"),
MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"),
MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"),
MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"),
MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"),
MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"),
MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"),
MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"),
MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"),
)

140
features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt

@ -20,7 +20,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -62,7 +61,6 @@ import io.element.android.libraries.matrix.ui.model.getBestName
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import io.element.android.libraries.ui.strings.R as StringR import io.element.android.libraries.ui.strings.R as StringR
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SelectUsersView( fun SelectUsersView(
state: SelectUsersState, state: SelectUsersState,
@ -71,79 +69,20 @@ fun SelectUsersView(
val eventSink = state.eventSink val eventSink = state.eventSink
Column( Column(
modifier = modifier modifier = modifier,
.fillMaxSize()
) { ) {
SearchUserBar( SearchUserBar(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
query = state.searchQuery, query = state.searchQuery,
results = state.searchResults, results = state.searchResults,
selectedUsers = state.selectedUsers,
active = state.isSearchActive, active = state.isSearchActive,
isMultiSelectionEnabled = state.isMultiSelectionEnabled,
onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) },
onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) },
onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) } onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) },
onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) },
) )
// TODO move into search content
SelectedUsersList(
modifier = Modifier.padding(16.dp),
selectedUsers = state.selectedUsers,
onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) }
)
}
}
@Composable
fun SelectedUsersList(
selectedUsers: List<MatrixUser>,
modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit = {},
) {
LazyRow(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
items(selectedUsers) { matrixUser ->
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
)
}
}
}
@Composable
fun SelectedUser(
matrixUser: MatrixUser,
modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit,
) {
Box(modifier = modifier.width(56.dp)) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56.dp)))
Text(
text = matrixUser.getBestName(),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.bodyLarge,
)
}
IconButton(
modifier = Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
.size(20.dp)
.align(Alignment.TopEnd),
onClick = { onUserRemoved(matrixUser) }
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(id = StringR.string.action_remove),
tint = MaterialTheme.colorScheme.onPrimary,
)
}
} }
} }
@ -152,12 +91,15 @@ fun SelectedUser(
fun SearchUserBar( fun SearchUserBar(
query: String, query: String,
results: ImmutableList<MatrixUser>, results: ImmutableList<MatrixUser>,
selectedUsers: ImmutableList<MatrixUser>,
active: Boolean, active: Boolean,
isMultiSelectionEnabled: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
placeHolderTitle: String = stringResource(StringR.string.search_for_someone), placeHolderTitle: String = stringResource(StringR.string.search_for_someone),
onActiveChanged: (Boolean) -> Unit = {}, onActiveChanged: (Boolean) -> Unit = {},
onTextChanged: (String) -> Unit = {}, onTextChanged: (String) -> Unit = {},
onResultSelected: (MatrixUser) -> Unit = {}, onResultSelected: (MatrixUser) -> Unit = {},
onUserRemoved: (MatrixUser) -> Unit = {},
) { ) {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
@ -206,6 +148,14 @@ fun SearchUserBar(
}, },
colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent),
content = { content = {
if (isMultiSelectionEnabled && selectedUsers.isNotEmpty()) {
SelectedUsersList(
modifier = Modifier.padding(16.dp),
selectedUsers = selectedUsers,
onUserRemoved = onUserRemoved,
)
}
LazyColumn { LazyColumn {
items(results) { items(results) {
SearchUserResultItem( SearchUserResultItem(
@ -232,14 +182,68 @@ fun SearchUserResultItem(
) )
} }
@Composable
fun SelectedUsersList(
selectedUsers: List<MatrixUser>,
modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit = {},
) {
LazyRow(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
items(selectedUsers) { matrixUser ->
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
)
}
}
}
@Composable
fun SelectedUser(
matrixUser: MatrixUser,
modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit,
) {
Box(modifier = modifier.width(56.dp)) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56.dp)))
Text(
text = matrixUser.getBestName(),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.bodyLarge,
)
}
IconButton(
modifier = Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
.size(20.dp)
.align(Alignment.TopEnd),
onClick = { onUserRemoved(matrixUser) }
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(id = StringR.string.action_remove),
tint = MaterialTheme.colorScheme.onPrimary,
)
}
}
}
@Preview @Preview
@Composable @Composable
internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = internal fun SelectUsersViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) =
ElementPreviewLight { ContentToPreview(state) } ElementPreviewLight { ContentToPreview(state) }
@Preview @Preview
@Composable @Composable
internal fun ChangeServerViewDarkPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = internal fun SelectUsersViewDarkPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) =
ElementPreviewDark { ContentToPreview(state) } ElementPreviewDark { ContentToPreview(state) }
@Composable @Composable

33
features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt

@ -0,0 +1,33 @@
/*
* 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.selectusers.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.selectusers.api.SelectMultipleUsersPresenter
import io.element.android.libraries.di.SessionScope
import javax.inject.Inject
// TODO add unit tests
@ContributesBinding(
scope = SessionScope::class,
boundType = SelectMultipleUsersPresenter::class,
)
class DefaultSelectMultipleUsersPresenter @Inject constructor() :
DefaultSelectUsersPresenter,
SelectMultipleUsersPresenter {
override val isMultiSelectionEnabled: Boolean = true
}

32
features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt

@ -0,0 +1,32 @@
/*
* 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.selectusers.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.selectusers.api.SelectSingleUserPresenter
import io.element.android.libraries.di.SessionScope
import javax.inject.Inject
@ContributesBinding(
scope = SessionScope::class,
boundType = SelectSingleUserPresenter::class,
)
class DefaultSelectSingleUserPresenter @Inject constructor() :
DefaultSelectUsersPresenter,
SelectSingleUserPresenter {
override val isMultiSelectionEnabled: Boolean = false
}

15
features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt

@ -24,26 +24,26 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.selectusers.api.SelectUsersEvents import io.element.android.features.selectusers.api.SelectUsersEvents
import io.element.android.features.selectusers.api.SelectUsersPresenter
import io.element.android.features.selectusers.api.SelectUsersState import io.element.android.features.selectusers.api.SelectUsersState
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.MatrixPatterns
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
@ContributesBinding(SessionScope::class) interface DefaultSelectUsersPresenter : Presenter<SelectUsersState> {
class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter {
val isMultiSelectionEnabled: Boolean
@Composable @Composable
override fun present(): SelectUsersState { override fun present(): SelectUsersState {
var isSearchActive by rememberSaveable { mutableStateOf(false) } var isSearchActive by rememberSaveable { mutableStateOf(false) }
val selectedUsers: MutableState<ImmutableList<MatrixUser>> = remember { mutableStateOf(persistentListOf()) } val selectedUsers: MutableState<ImmutableList<MatrixUser>> = remember {
mutableStateOf(persistentListOf())
}
var searchQuery by rememberSaveable { mutableStateOf("") } var searchQuery by rememberSaveable { mutableStateOf("") }
val searchResults: MutableState<ImmutableList<MatrixUser>> = remember { val searchResults: MutableState<ImmutableList<MatrixUser>> = remember {
mutableStateOf(persistentListOf()) mutableStateOf(persistentListOf())
@ -76,6 +76,7 @@ class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter {
searchResults = searchResults.value, searchResults = searchResults.value,
selectedUsers = selectedUsers.value, selectedUsers = selectedUsers.value,
isSearchActive = isSearchActive, isSearchActive = isSearchActive,
isMultiSelectionEnabled = isMultiSelectionEnabled,
eventSink = ::handleEvents, eventSink = ::handleEvents,
) )
} }

6
features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt → features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt

@ -28,11 +28,11 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class DefaultSelectUsersPresenterTests { class DefaultSelectSingleUserPresenterTests {
@Test @Test
fun `present - initial state`() = runTest { fun `present - initial state`() = runTest {
val presenter = DefaultSelectUsersPresenter() val presenter = DefaultSelectSingleUserPresenter()
moleculeFlow(RecompositionClock.Immediate) { moleculeFlow(RecompositionClock.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
@ -43,7 +43,7 @@ class DefaultSelectUsersPresenterTests {
@Test @Test
fun `present - update search query`() = runTest { fun `present - update search query`() = runTest {
val presenter = DefaultSelectUsersPresenter() val presenter = DefaultSelectSingleUserPresenter()
moleculeFlow(RecompositionClock.Immediate) { moleculeFlow(RecompositionClock.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
Loading…
Cancel
Save