[Room member list] Display room member list (#276)

* Implement room member list

* Move timeline initialization back to `TimelinePresenter`.

* Fix crash when the `innerRoom` inside a `RustMatrixRoom` is destroyed but `syncUpdateFlow` is still running.

* Address review comments
This commit is contained in:
Jorge Martin Espinosa
2023-04-04 18:07:57 +02:00
committed by GitHub
parent f0b95d30be
commit d7a6779343
62 changed files with 1159 additions and 157 deletions

View File

@@ -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.createroom.impl
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import javax.inject.Inject
// TODO this is empty as we currently don't have an endpoint to perform user search
class AllMatrixUsersDataSource @Inject constructor() : MatrixUserDataSource {
override suspend fun search(query: String): List<MatrixUser> {
return emptyList()
}
override suspend fun getProfile(userId: UserId): MatrixUser? {
return null
}
}

View File

@@ -17,31 +17,38 @@
package io.element.android.features.createroom.impl.addpeople
import androidx.compose.runtime.Composable
import io.element.android.features.selectusers.api.SelectUsersPresenter
import io.element.android.features.selectusers.api.SelectUsersPresenterArgs
import io.element.android.features.selectusers.api.SelectionMode
import io.element.android.features.userlist.api.SelectionMode
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.features.userlist.api.UserListPresenter
import io.element.android.features.userlist.api.UserListPresenterArgs
import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject
import javax.inject.Named
class AddPeoplePresenter @Inject constructor(
private val selectUsersPresenterFactory: SelectUsersPresenter.Factory,
private val userListPresenterFactory: UserListPresenter.Factory,
@Named("AllUsers") private val matrixUserDataSource: MatrixUserDataSource,
) : Presenter<AddPeopleState> {
private val selectUsersPresenter by lazy {
selectUsersPresenterFactory.create(SelectUsersPresenterArgs(SelectionMode.Multiple))
private val userListPresenter by lazy {
userListPresenterFactory.create(
UserListPresenterArgs(selectionMode = SelectionMode.Multiple),
matrixUserDataSource,
)
}
@Composable
override fun present(): AddPeopleState {
val selectUsersState = selectUsersPresenter.present()
val userListState = userListPresenter.present()
fun handleEvents(event: AddPeopleEvents) {
// do nothing for now
}
return AddPeopleState(
selectUsersState = selectUsersState,
userListState = userListState,
eventSink = ::handleEvents,
)
}
}

View File

@@ -16,9 +16,9 @@
package io.element.android.features.createroom.impl.addpeople
import io.element.android.features.selectusers.api.SelectUsersState
import io.element.android.features.userlist.api.UserListState
data class AddPeopleState(
val selectUsersState: SelectUsersState,
val userListState: UserListState,
val eventSink: (AddPeopleEvents) -> Unit,
)

View File

@@ -17,22 +17,22 @@
package io.element.android.features.createroom.impl.addpeople
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.selectusers.api.SelectionMode
import io.element.android.features.selectusers.api.aListOfSelectedUsers
import io.element.android.features.selectusers.api.aSelectUsersState
import io.element.android.features.userlist.api.SelectionMode
import io.element.android.features.userlist.api.aListOfSelectedUsers
import io.element.android.features.userlist.api.aUserListState
open class AddPeopleStateProvider : PreviewParameterProvider<AddPeopleState> {
override val values: Sequence<AddPeopleState>
get() = sequenceOf(
aAddPeopleState(),
aAddPeopleState().copy(
selectUsersState = aSelectUsersState().copy(
userListState = aUserListState().copy(
selectedUsers = aListOfSelectedUsers(),
selectionMode = SelectionMode.Multiple,
)
),
aAddPeopleState().copy(
selectUsersState = aSelectUsersState().copy(
userListState = aUserListState().copy(
selectedUsers = aListOfSelectedUsers(),
isSearchActive = true,
selectionMode = SelectionMode.Multiple,
@@ -42,6 +42,6 @@ open class AddPeopleStateProvider : PreviewParameterProvider<AddPeopleState> {
}
fun aAddPeopleState() = AddPeopleState(
selectUsersState = aSelectUsersState(),
userListState = aUserListState(),
eventSink = {}
)

View File

@@ -29,8 +29,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.userlist.api.UserListView
import io.element.android.features.createroom.impl.R
import io.element.android.features.selectusers.api.SelectUsersView
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@@ -52,9 +52,9 @@ fun AddPeopleView(
Scaffold(
topBar = {
if (!state.selectUsersState.isSearchActive) {
if (!state.userListState.isSearchActive) {
AddPeopleViewTopBar(
hasSelectedUsers = state.selectUsersState.selectedUsers.isNotEmpty(),
hasSelectedUsers = state.userListState.selectedUsers.isNotEmpty(),
onBackPressed = onBackPressed,
onNextPressed = onNextPressed,
)
@@ -66,9 +66,9 @@ fun AddPeopleView(
.fillMaxSize()
.padding(padding),
) {
SelectUsersView(
UserListView(
modifier = Modifier.fillMaxWidth(),
state = state.selectUsersState,
state = state.userListState,
)
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.createroom.impl.di
import com.squareup.anvil.annotations.ContributesTo
import dagger.Binds
import dagger.Module
import io.element.android.features.createroom.impl.AllMatrixUsersDataSource
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.libraries.di.AppScope
import javax.inject.Named
@Module
@ContributesTo(AppScope::class)
interface CreateRoomModule {
@Binds
@Named("AllUsers")
fun bindAllUserListDataSource(dataSource: AllMatrixUsersDataSource): MatrixUserDataSource
}

View File

@@ -17,25 +17,31 @@
package io.element.android.features.createroom.impl.root
import androidx.compose.runtime.Composable
import io.element.android.features.selectusers.api.SelectUsersPresenter
import io.element.android.features.selectusers.api.SelectUsersPresenterArgs
import io.element.android.features.selectusers.api.SelectionMode
import io.element.android.features.userlist.api.SelectionMode
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.features.userlist.api.UserListPresenter
import io.element.android.features.userlist.api.UserListPresenterArgs
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.ui.model.MatrixUser
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class CreateRoomRootPresenter @Inject constructor(
private val presenterFactory: SelectUsersPresenter.Factory,
private val presenterFactory: UserListPresenter.Factory,
@Named("AllUsers") private val matrixUserDataSource: MatrixUserDataSource,
) : Presenter<CreateRoomRootState> {
private val presenter by lazy {
presenterFactory.create(SelectUsersPresenterArgs(SelectionMode.Single))
presenterFactory.create(
UserListPresenterArgs(selectionMode = SelectionMode.Single),
matrixUserDataSource,
)
}
@Composable
override fun present(): CreateRoomRootState {
val selectUsersState = presenter.present()
val userListState = presenter.present()
fun handleEvents(event: CreateRoomRootEvents) {
when (event) {
@@ -45,7 +51,7 @@ class CreateRoomRootPresenter @Inject constructor(
}
return CreateRoomRootState(
selectUsersState = selectUsersState,
userListState = userListState,
eventSink = ::handleEvents,
)
}

View File

@@ -16,9 +16,9 @@
package io.element.android.features.createroom.impl.root
import io.element.android.features.selectusers.api.SelectUsersState
import io.element.android.features.userlist.api.UserListState
data class CreateRoomRootState(
val selectUsersState: SelectUsersState,
val userListState: UserListState,
val eventSink: (CreateRoomRootEvents) -> Unit,
)

View File

@@ -17,7 +17,7 @@
package io.element.android.features.createroom.impl.root
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.selectusers.api.aSelectUsersState
import io.element.android.features.userlist.api.aUserListState
open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRootState> {
override val values: Sequence<CreateRoomRootState>
@@ -28,5 +28,5 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider<CreateRoomRoot
fun aCreateRoomRootState() = CreateRoomRootState(
eventSink = {},
selectUsersState = aSelectUsersState(),
userListState = aUserListState(),
)

View File

@@ -37,8 +37,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.userlist.api.UserListView
import io.element.android.features.createroom.impl.R
import io.element.android.features.selectusers.api.SelectUsersView
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
@@ -60,7 +60,7 @@ fun CreateRoomRootView(
Scaffold(
modifier = modifier.fillMaxWidth(),
topBar = {
if (!state.selectUsersState.isSearchActive) {
if (!state.userListState.isSearchActive) {
CreateRoomRootViewTopBar(onClosePressed = onClosePressed)
}
}
@@ -69,13 +69,13 @@ fun CreateRoomRootView(
modifier = Modifier.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
SelectUsersView(
UserListView(
modifier = Modifier.fillMaxWidth(),
state = state.selectUsersState,
state = state.userListState,
onUserSelected = { state.eventSink.invoke(CreateRoomRootEvents.StartDM(it)) },
)
if (!state.selectUsersState.isSearchActive) {
if (!state.userListState.isSearchActive) {
CreateRoomActionButtonsList(
onNewRoomClicked = onNewRoomClicked,
onInvitePeopleClicked = { state.eventSink(CreateRoomRootEvents.InvitePeople) },

View File

@@ -22,8 +22,10 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.selectusers.api.SelectUsersPresenterArgs
import io.element.android.features.selectusers.impl.DefaultSelectUsersPresenter
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.features.userlist.api.UserListPresenterArgs
import io.element.android.features.userlist.impl.DefaultUserListPresenter
import io.element.android.features.userlist.test.FakeMatrixUserDataSource
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -35,10 +37,11 @@ class AddPeoplePresenterTests {
@Before
fun setup() {
val selectUsersFactory = object : DefaultSelectUsersPresenter.DefaultSelectUsersFactory {
override fun create(args: SelectUsersPresenterArgs) = DefaultSelectUsersPresenter(args)
val userListFactory = object : DefaultUserListPresenter.DefaultUserListFactory {
override fun create(args: UserListPresenterArgs, dataSource: MatrixUserDataSource) = DefaultUserListPresenter(args, dataSource)
}
presenter = AddPeoplePresenter(selectUsersFactory)
val dataSource = FakeMatrixUserDataSource()
presenter = AddPeoplePresenter(userListFactory, dataSource)
}
@Test
@@ -51,3 +54,4 @@ class AddPeoplePresenterTests {
}
}
}

View File

@@ -22,8 +22,10 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.selectusers.api.SelectUsersPresenterArgs
import io.element.android.features.selectusers.impl.DefaultSelectUsersPresenter
import io.element.android.features.userlist.api.MatrixUserDataSource
import io.element.android.features.userlist.api.UserListPresenterArgs
import io.element.android.features.userlist.impl.DefaultUserListPresenter
import io.element.android.features.userlist.test.FakeMatrixUserDataSource
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,14 +35,16 @@ import org.junit.Test
class CreateRoomRootPresenterTests {
private lateinit var userListDataSource: FakeMatrixUserDataSource
private lateinit var presenter: CreateRoomRootPresenter
@Before
fun setup() {
val selectUsersPresenter = object : DefaultSelectUsersPresenter.DefaultSelectUsersFactory {
override fun create(args: SelectUsersPresenterArgs) = DefaultSelectUsersPresenter(args)
val userListPresenter = object : DefaultUserListPresenter.DefaultUserListFactory {
override fun create(args: UserListPresenterArgs, dataSource: MatrixUserDataSource) = DefaultUserListPresenter(args, dataSource)
}
presenter = CreateRoomRootPresenter(selectUsersPresenter)
userListDataSource = FakeMatrixUserDataSource()
presenter = CreateRoomRootPresenter(userListPresenter, userListDataSource)
}
@Test