From f5f322c14c6cbc2250da569909be32244b34e8d4 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Tue, 11 Apr 2023 22:29:16 +0200 Subject: [PATCH 01/24] Add create room API --- .../libraries/matrix/api/MatrixClient.kt | 4 +- .../api/createroom/CreateRoomParameters.kt | 30 +++++++++ .../matrix/api/createroom/RoomPreset.kt | 22 +++++++ .../matrix/api/createroom/RoomVisibility.kt | 21 +++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 63 +++++++++++++------ .../libraries/matrix/test/FakeMatrixClient.kt | 14 +++-- 6 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomPreset.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomVisibility.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 5a9e2a4bb6..41e86f2701 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService @@ -32,8 +33,9 @@ interface MatrixClient : Closeable { val sessionId: SessionId val roomSummaryDataSource: RoomSummaryDataSource fun getRoom(roomId: RoomId): MatrixRoom? - suspend fun createDM(userId: UserId): Result fun findDM(userId: UserId): MatrixRoom? + suspend fun createRoom(createRoomParams: CreateRoomParameters): Result + suspend fun createDM(userId: UserId): Result fun startSync() fun stopSync() fun mediaResolver(): MediaResolver diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt new file mode 100644 index 0000000000..c65aae6156 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/CreateRoomParameters.kt @@ -0,0 +1,30 @@ +/* + * 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.libraries.matrix.api.createroom + +import io.element.android.libraries.matrix.api.core.UserId + +data class CreateRoomParameters( + val name: String?, + val topic: String? = null, + val isEncrypted: Boolean, + val isDirect: Boolean = false, + val visibility: RoomVisibility, + val preset: RoomPreset, + val invite: List? = null, + val avatar: String? = null, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomPreset.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomPreset.kt new file mode 100644 index 0000000000..c2254e395f --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomPreset.kt @@ -0,0 +1,22 @@ +/* + * 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.libraries.matrix.api.createroom + +enum class RoomPreset { + PRIVATE_CHAT, + PUBLIC_CHAT, + TRUSTED_PRIVATE_CHAT, +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomVisibility.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomVisibility.kt new file mode 100644 index 0000000000..d2715363e8 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/createroom/RoomVisibility.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.libraries.matrix.api.createroom + +enum class RoomVisibility { + PUBLIC, + PRIVATE, +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index d2408abdd7..bcac6cebd1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -20,6 +20,9 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters +import io.element.android.libraries.matrix.api.createroom.RoomPreset +import io.element.android.libraries.matrix.api.createroom.RoomVisibility import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService @@ -42,10 +45,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate -import org.matrix.rustcomponents.sdk.CreateRoomParameters import org.matrix.rustcomponents.sdk.RequiredState -import org.matrix.rustcomponents.sdk.RoomPreset -import org.matrix.rustcomponents.sdk.RoomVisibility import org.matrix.rustcomponents.sdk.SlidingSyncListBuilder import org.matrix.rustcomponents.sdk.SlidingSyncMode import org.matrix.rustcomponents.sdk.SlidingSyncRequestListFilters @@ -55,6 +55,9 @@ import org.matrix.rustcomponents.sdk.use import timber.log.Timber import java.io.File import java.util.concurrent.atomic.AtomicBoolean +import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParameters +import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset +import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility class RustMatrixClient constructor( private val client: Client, @@ -175,24 +178,46 @@ class RustMatrixClient constructor( return roomId?.let { getRoom(it) } } - override suspend fun createDM(userId: UserId): Result = - withContext(dispatchers.io) { - runCatching { - val roomId = client.createRoom( - CreateRoomParameters( - name = null, - topic = null, - isEncrypted = true, - isDirect = true, - visibility = RoomVisibility.PRIVATE, - preset = RoomPreset.TRUSTED_PRIVATE_CHAT, - invite = listOf(userId.value), - avatar = null, - ) + override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result = withContext(dispatchers.io) { + runCatching { + val roomId = client.createRoom( + RustCreateRoomParameters( + name = createRoomParams.name, + topic = createRoomParams.topic, + isEncrypted = createRoomParams.isEncrypted, + isDirect = createRoomParams.isDirect, + visibility = when (createRoomParams.visibility) { + RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC + RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE + }, + preset = when (createRoomParams.preset) { + RoomPreset.PRIVATE_CHAT -> RustRoomPreset.PRIVATE_CHAT + RoomPreset.PUBLIC_CHAT -> RustRoomPreset.PUBLIC_CHAT + RoomPreset.TRUSTED_PRIVATE_CHAT -> RustRoomPreset.TRUSTED_PRIVATE_CHAT + }, + invite = createRoomParams.invite?.map { it.value }, + avatar = createRoomParams.avatar, ) - RoomId(roomId) - } + ) + RoomId(roomId) } + } + + override suspend fun createDM(userId: UserId): Result = withContext(dispatchers.io) { + runCatching { + val roomId = client.createRoom( + RustCreateRoomParameters( + name = null, + isEncrypted = true, + isDirect = true, + visibility = RustRoomVisibility.PRIVATE, + preset = RustRoomPreset.TRUSTED_PRIVATE_CHAT, + invite = listOf(userId.value), + ) + ) + RoomId(roomId) + } + } override fun mediaResolver(): MediaResolver = mediaResolver diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 7159eff417..a6741982ea 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService @@ -54,16 +55,21 @@ class FakeMatrixClient( return FakeMatrixRoom(roomId) } + override fun findDM(userId: UserId): MatrixRoom? { + return findDmResult + } + + override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result { + delay(100) + return Result.success(A_ROOM_ID) + } + override suspend fun createDM(userId: UserId): Result { delay(100) createDmFailure?.let { throw it } return createDmResult } - override fun findDM(userId: UserId): MatrixRoom? { - return findDmResult - } - override fun startSync() = Unit override fun stopSync() = Unit From ee0b8edf5eba648ad760524d6a5984f5f1a40695 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 12 Apr 2023 14:14:48 +0200 Subject: [PATCH 02/24] Add CreateRoomScope with data store --- .../createroom/impl/CreateRoomConfig.kt | 30 +++++++++++++ .../createroom/impl/CreateRoomDataStore.kt | 35 +++++++++++++++ .../createroom/impl/CreateRoomFlowNode.kt | 35 ++++++++++----- .../impl/addpeople/AddPeopleNode.kt | 4 +- .../impl/addpeople/AddPeoplePresenter.kt | 12 ++++- .../impl/configureroom/ConfigureRoomNode.kt | 18 ++------ .../configureroom/ConfigureRoomPresenter.kt | 44 +++++++------------ .../impl/configureroom/ConfigureRoomState.kt | 10 +---- .../ConfigureRoomStateProvider.kt | 9 +--- .../impl/configureroom/ConfigureRoomView.kt | 11 ++--- .../createroom/impl/di/CreateRoomComponent.kt | 39 ++++++++++++++++ .../createroom/impl/di/CreateRoomScope.kt | 19 ++++++++ 12 files changed, 186 insertions(+), 80 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomComponent.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomScope.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt new file mode 100644 index 0000000000..0aedf12d0f --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomConfig.kt @@ -0,0 +1,30 @@ +/* + * 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.createroom.impl.configureroom.RoomPrivacy +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +data class CreateRoomConfig( + val roomName: String? = null, + val topic: String? = null, + val avatarUrl: String? = null, + val invites: ImmutableList = persistentListOf(), + val privacy: RoomPrivacy? = null, +) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt new file mode 100644 index 0000000000..ed61431511 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -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 + +import io.element.android.features.createroom.impl.di.CreateRoomScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@SingleIn(CreateRoomScope::class) +class CreateRoomDataStore @Inject constructor() { + + private val createRoomConfigFlow: MutableStateFlow = MutableStateFlow(CreateRoomConfig()) + + fun getCreateRoomConfig(): Flow = createRoomConfigFlow + + fun setCreateRoomConfig(createRoomConfig: CreateRoomConfig) { + createRoomConfigFlow.tryEmit(createRoomConfig) + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index a50c9ab1d0..c663814c19 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -32,10 +32,13 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.impl.addpeople.AddPeopleNode import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode +import io.element.android.features.createroom.impl.di.CreateRoomComponent import io.element.android.features.createroom.impl.root.CreateRoomRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -45,14 +48,22 @@ import kotlinx.parcelize.Parcelize class CreateRoomFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : BackstackNode( - backstack = BackStack( - initialElement = NavTarget.Root, - savedStateMap = buildContext.savedStateMap, - ), - buildContext = buildContext, - plugins = plugins -) { +) : DaggerComponentOwner, + BackstackNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins + ) { + + private val component by lazy { + parent!!.bindings().createRoomComponentBuilder().build() + } + + override val daggerComponent: Any + get() = component sealed interface NavTarget : Parcelable { @Parcelize @@ -62,7 +73,7 @@ class CreateRoomFlowNode @AssistedInject constructor( object NewRoom : NavTarget @Parcelize - data class ConfigureRoom(val users: List) : NavTarget + object ConfigureRoom : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -82,13 +93,13 @@ class CreateRoomFlowNode @AssistedInject constructor( NavTarget.NewRoom -> { val callback = object : AddPeopleNode.Callback { override fun onContinue(selectedUsers: List) { - backstack.push(NavTarget.ConfigureRoom(selectedUsers)) + backstack.push(NavTarget.ConfigureRoom) } } createNode(context = buildContext, plugins = listOf(callback)) } - is NavTarget.ConfigureRoom -> { - createNode(context = buildContext, plugins = listOf(ConfigureRoomNode.Inputs(navTarget.users))) + NavTarget.ConfigureRoom -> { + createNode(context = buildContext) } } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index 91b1d5a721..2d060a6644 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -25,10 +25,10 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.SessionScope +import io.element.android.features.createroom.impl.di.CreateRoomScope import io.element.android.libraries.matrix.ui.model.MatrixUser -@ContributesNode(SessionScope::class) +@ContributesNode(CreateRoomScope::class) class AddPeopleNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index da51a36335..7090a642af 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -17,8 +17,12 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable -import io.element.android.features.userlist.api.SelectionMode +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import io.element.android.features.createroom.impl.CreateRoomConfig +import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Presenter @@ -28,6 +32,7 @@ import javax.inject.Named class AddPeoplePresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, @Named("AllUsers") private val matrixUserDataSource: MatrixUserDataSource, + private val dataStore: CreateRoomDataStore, ) : Presenter { private val userListPresenter by lazy { @@ -40,7 +45,10 @@ class AddPeoplePresenter @Inject constructor( @Composable override fun present(): AddPeopleState { val userListState = userListPresenter.present() - + val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) + LaunchedEffect(userListState.selectedUsers) { + dataStore.setCreateRoomConfig(createRoomConfig.value.copy(invites = userListState.selectedUsers)) + } fun handleEvents(event: AddPeopleEvents) { // do nothing for now } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index ec5bf67787..08a4f5f64b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -24,27 +24,15 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.ui.model.MatrixUser +import io.element.android.features.createroom.impl.di.CreateRoomScope -@ContributesNode(SessionScope::class) +@ContributesNode(CreateRoomScope::class) class ConfigureRoomNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenterFactory: ConfigureRoomPresenter.Factory, + private val presenter: ConfigureRoomPresenter, ) : Node(buildContext, plugins = plugins) { - data class Inputs( - val selectedUsers: List - ) : NodeInputs - - private val inputs: Inputs = inputs() - private val presenter by lazy { - presenterFactory.create(ConfigureRoomPresenterArgs(inputs.selectedUsers)) - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index c8bad29f93..7a877a872b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -16,54 +16,40 @@ package io.element.android.features.createroom.impl.configureroom -import android.net.Uri import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject +import io.element.android.features.createroom.impl.CreateRoomConfig +import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter -import kotlinx.collections.immutable.toImmutableList +import javax.inject.Inject -class ConfigureRoomPresenter @AssistedInject constructor( - @Assisted val args: ConfigureRoomPresenterArgs, +class ConfigureRoomPresenter @Inject constructor( + private val dataStore: CreateRoomDataStore, ) : Presenter { - @AssistedFactory - interface Factory { - fun create(args: ConfigureRoomPresenterArgs): ConfigureRoomPresenter - } - @Composable override fun present(): ConfigureRoomState { - var roomName by rememberSaveable { mutableStateOf("") } - var topic by rememberSaveable { mutableStateOf("") } - var avatarUri by rememberSaveable { mutableStateOf(null) } - var privacy by rememberSaveable { mutableStateOf(null) } - val isCreateButtonEnabled by rememberSaveable(roomName, privacy) { - val enabled = roomName.isNotEmpty() && privacy != null + val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) + val isCreateButtonEnabled by rememberSaveable(createRoomConfig.value.roomName, createRoomConfig.value.privacy) { + val enabled = createRoomConfig.value.roomName.isNullOrEmpty().not() && createRoomConfig.value.privacy != null mutableStateOf(enabled) } fun handleEvents(event: ConfigureRoomEvents) { when (event) { - is ConfigureRoomEvents.AvatarUriChanged -> avatarUri = event.uri - is ConfigureRoomEvents.RoomNameChanged -> roomName = event.name - is ConfigureRoomEvents.TopicChanged -> topic = event.topic - is ConfigureRoomEvents.RoomPrivacyChanged -> privacy = event.privacy - ConfigureRoomEvents.CreateRoom -> Unit // TODO + is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(avatarUrl = event.uri?.toString())) + is ConfigureRoomEvents.RoomNameChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name)) + is ConfigureRoomEvents.TopicChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) + is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) + ConfigureRoomEvents.CreateRoom -> Unit } } return ConfigureRoomState( - selectedUsers = args.selectedUsers.toImmutableList(), - roomName = roomName, - topic = topic, - avatarUri = avatarUri, - privacy = privacy, + createRoomConfig.value, isCreateButtonEnabled = isCreateButtonEnabled, eventSink = ::handleEvents, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 23f5691267..47be0b1f17 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -16,16 +16,10 @@ package io.element.android.features.createroom.impl.configureroom -import android.net.Uri -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList +import io.element.android.features.createroom.impl.CreateRoomConfig data class ConfigureRoomState( - val selectedUsers: ImmutableList, - val roomName: String, - val topic: String, - val avatarUri: Uri?, - val privacy: RoomPrivacy?, + val config: CreateRoomConfig, val isCreateButtonEnabled: Boolean, val eventSink: (ConfigureRoomEvents) -> Unit ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index 3e4961f56a..b1614381ae 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -17,8 +17,7 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import kotlinx.collections.immutable.toImmutableList +import io.element.android.features.createroom.impl.CreateRoomConfig open class ConfigureRoomStateProvider : PreviewParameterProvider { override val values: Sequence @@ -28,11 +27,7 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider Date: Wed, 12 Apr 2023 14:17:15 +0200 Subject: [PATCH 03/24] Add RemoveFromSelection event in room configuration screen --- .../createroom/impl/configureroom/ConfigureRoomEvents.kt | 2 ++ .../impl/configureroom/ConfigureRoomPresenter.kt | 8 ++++++++ .../createroom/impl/configureroom/ConfigureRoomView.kt | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index 644c3d1e03..f10f673d78 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -17,11 +17,13 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri +import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface ConfigureRoomEvents { data class RoomNameChanged(val name: String) : ConfigureRoomEvents data class TopicChanged(val topic: String) : ConfigureRoomEvents data class AvatarUriChanged(val uri: Uri?) : ConfigureRoomEvents data class RoomPrivacyChanged(val privacy: RoomPrivacy?) : ConfigureRoomEvents + data class RemoveFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents object CreateRoom : ConfigureRoomEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 7a877a872b..53523449f7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter +import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -44,6 +45,13 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.RoomNameChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name)) is ConfigureRoomEvents.TopicChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) + is ConfigureRoomEvents.RemoveFromSelection -> dataStore.setCreateRoomConfig( + createRoomConfig.value.copy( + invites = createRoomConfig.value.invites.minus( + event.matrixUser + ).toImmutableList() + ) + ) ConfigureRoomEvents.CreateRoom -> Unit } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index d0cf2e310d..0e8167fea4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -106,7 +106,7 @@ fun ConfigureRoomView( listState = selectedUsersListState, contentPadding = PaddingValues(horizontal = 24.dp), selectedUsers = state.config.invites, - onUserRemoved = { }, // TODO + onUserRemoved = { state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) }, ) Spacer(Modifier.weight(1f)) RoomPrivacyOptions( From 552f66a92ad98c06b2aaf53a1078fbbc77241966 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 12 Apr 2023 16:31:51 +0200 Subject: [PATCH 04/24] Split user list views into multiple files --- .../impl/addpeople/AddPeopleView.kt | 2 +- .../impl/configureroom/ConfigureRoomView.kt | 2 +- .../impl/root/CreateRoomRootView.kt | 2 +- .../impl/members/RoomMemberListView.kt | 4 +- .../userlist/api/UserListStateProvider.kt | 31 +- .../features/userlist/api/UserListView.kt | 314 ------------------ .../SearchMultipleUsersResultItem.kt | 61 ++++ .../components/SearchSingleUserResultItem.kt | 55 +++ .../userlist/api/components/SearchUserBar.kt | 146 ++++++++ .../userlist/api/components/SelectedUser.kt | 94 ++++++ .../api/components/SelectedUsersList.kt | 71 ++++ .../userlist/api/components/UserListView.kt | 91 +++++ 12 files changed, 528 insertions(+), 345 deletions(-) delete mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListView.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchMultipleUsersResultItem.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchSingleUserResultItem.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUser.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index eed59923a8..9de3e0fa20 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.createroom.impl.R -import io.element.android.features.userlist.api.UserListView +import io.element.android.features.userlist.api.components.UserListView 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 diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 0e8167fea4..ddb000d883 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -55,7 +55,7 @@ import androidx.core.net.toUri import coil.compose.AsyncImage import coil.request.ImageRequest import io.element.android.features.createroom.impl.R -import io.element.android.features.userlist.api.SelectedUsersList +import io.element.android.features.userlist.api.components.SelectedUsersList 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 diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index bcf58b7d2d..a94b3bf28b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.createroom.impl.R -import io.element.android.features.userlist.api.UserListView +import io.element.android.features.userlist.api.components.UserListView import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.RetryDialog diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index e2c41e34b3..f356e203f2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -39,8 +39,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.roomdetails.impl.R -import io.element.android.features.userlist.api.SearchSingleUserResultItem -import io.element.android.features.userlist.api.UserListView +import io.element.android.features.userlist.api.components.SearchSingleUserResultItem +import io.element.android.features.userlist.api.components.UserListView import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.isLoading import io.element.android.libraries.designsystem.ElementTextStyles diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt index d97a4537ed..80207fb4bc 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt @@ -18,9 +18,9 @@ package io.element.android.features.userlist.api import androidx.compose.foundation.lazy.LazyListState import androidx.compose.ui.tooling.preview.PreviewParameterProvider -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.components.aMatrixUserList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList open class UserListStateProvider : PreviewParameterProvider { override val values: Sequence @@ -38,14 +38,14 @@ open class UserListStateProvider : PreviewParameterProvider { isSearchActive = true, searchQuery = "@someone:matrix.org", selectedUsers = aListOfSelectedUsers(), - searchResults = aListOfResults(), + searchResults = aMatrixUserList().toImmutableList(), ), aUserListState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", selectionMode = SelectionMode.Multiple, selectedUsers = aListOfSelectedUsers(), - searchResults = aListOfResults(), + searchResults = aMatrixUserList().toImmutableList(), ) ) } @@ -63,25 +63,4 @@ fun aUserListState() = UserListState( eventSink = {} ) -fun aListOfSelectedUsers() = persistentListOf( - MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@other:matrix.org"), username = "other"), -) - -fun aListOfResults() = persistentListOf( - MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@other:matrix.org"), username = "other"), - 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"), -) +fun aListOfSelectedUsers() = aMatrixUserList().take(4).toImmutableList() diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListView.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListView.kt deleted file mode 100644 index 5f587b471e..0000000000 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListView.kt +++ /dev/null @@ -1,314 +0,0 @@ -/* - * 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.userlist.api - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SearchBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.components.avatar.Avatar -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.ElementPreviewLight -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.SearchBar -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.ui.components.CheckableMatrixUserRow -import io.element.android.libraries.matrix.ui.components.MatrixUserRow -import io.element.android.libraries.matrix.ui.model.MatrixUser -import io.element.android.libraries.matrix.ui.model.getBestName -import kotlinx.collections.immutable.ImmutableList -import io.element.android.libraries.ui.strings.R as StringR - -@Composable -fun UserListView( - state: UserListState, - modifier: Modifier = Modifier, - onUserSelected: (MatrixUser) -> Unit = {}, - onUserDeselected: (MatrixUser) -> Unit = {}, -) { - Column( - modifier = modifier, - ) { - SearchUserBar( - modifier = Modifier.fillMaxWidth(), - query = state.searchQuery, - results = state.searchResults, - selectedUsers = state.selectedUsers, - selectedUsersListState = state.selectedUsersListState, - active = state.isSearchActive, - isMultiSelectionEnabled = state.isMultiSelectionEnabled, - onActiveChanged = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) }, - onTextChanged = { state.eventSink(UserListEvents.UpdateSearchQuery(it)) }, - onUserSelected = { - state.eventSink(UserListEvents.AddToSelection(it)) - onUserSelected(it) - }, - onUserDeselected = { - state.eventSink(UserListEvents.RemoveFromSelection(it)) - onUserDeselected(it) - }, - ) - - if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { - SelectedUsersList( - listState = state.selectedUsersListState, - contentPadding = PaddingValues(16.dp), - selectedUsers = state.selectedUsers, - onUserRemoved = { - state.eventSink(UserListEvents.RemoveFromSelection(it)) - onUserDeselected(it) - }, - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchUserBar( - query: String, - results: ImmutableList, - selectedUsers: ImmutableList, - selectedUsersListState: LazyListState, - active: Boolean, - isMultiSelectionEnabled: Boolean, - modifier: Modifier = Modifier, - placeHolderTitle: String = stringResource(StringR.string.common_search_for_someone), - onActiveChanged: (Boolean) -> Unit = {}, - onTextChanged: (String) -> Unit = {}, - onUserSelected: (MatrixUser) -> Unit = {}, - onUserDeselected: (MatrixUser) -> Unit = {}, -) { - val focusManager = LocalFocusManager.current - - if (!active) { - onTextChanged("") - focusManager.clearFocus() - } - - SearchBar( - query = query, - onQueryChange = onTextChanged, - onSearch = { focusManager.clearFocus() }, - active = active, - onActiveChange = onActiveChanged, - modifier = modifier - .padding(horizontal = if (!active) 16.dp else 0.dp), - placeholder = { - Text( - text = placeHolderTitle, - modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) - ) - }, - leadingIcon = if (active) { - { BackButton(onClick = { onActiveChanged(false) }) } - } else { - null - }, - trailingIcon = when { - active && query.isNotEmpty() -> { - { - IconButton(onClick = { onTextChanged("") }) { - Icon(Icons.Default.Close, stringResource(StringR.string.action_clear)) - } - } - } - !active -> { - { - Icon( - imageVector = Icons.Default.Search, - contentDescription = stringResource(StringR.string.action_search), - modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) - ) - } - } - else -> null - }, - colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), - content = { - if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { - SelectedUsersList( - listState = selectedUsersListState, - contentPadding = PaddingValues(16.dp), - selectedUsers = selectedUsers, - onUserRemoved = onUserDeselected, - ) - } - - LazyColumn { - if (isMultiSelectionEnabled) { - items(results) { matrixUser -> - SearchMultipleUsersResultItem( - modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - isUserSelected = selectedUsers.find { it.id == matrixUser.id } != null, - onCheckedChange = { checked -> - if (checked) { - onUserSelected(matrixUser) - } else { - onUserDeselected(matrixUser) - } - } - ) - } - } else { - items(results) { matrixUser -> - SearchSingleUserResultItem( - modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - onClick = { onUserSelected(matrixUser) } - ) - } - } - } - }, - ) -} - -@Composable -fun SearchMultipleUsersResultItem( - matrixUser: MatrixUser, - isUserSelected: Boolean, - modifier: Modifier = Modifier, - onCheckedChange: (Boolean) -> Unit, -) { - CheckableMatrixUserRow( - checked = isUserSelected, - modifier = modifier, - matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36.dp), - onCheckedChange = onCheckedChange, - ) -} - -@Composable -fun SearchSingleUserResultItem( - matrixUser: MatrixUser, - modifier: Modifier = Modifier, - onClick: () -> Unit = {}, -) { - MatrixUserRow( - modifier = modifier.clickable(onClick = onClick), - matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36.dp), - ) -} - -@Composable -fun SelectedUsersList( - listState: LazyListState, - selectedUsers: ImmutableList, - modifier: Modifier = Modifier, - contentPadding: PaddingValues = PaddingValues(0.dp), - onUserRemoved: (MatrixUser) -> Unit = {}, -) { - LazyRow( - state = listState, - modifier = modifier, - contentPadding = contentPadding, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - items(selectedUsers.toList()) { 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 -@Composable -internal fun UserListViewLightPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = - ElementPreviewLight { ContentToPreview(state) } - -@Preview -@Composable -internal fun UserListViewDarkPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = - ElementPreviewDark { ContentToPreview(state) } - -@Composable -private fun ContentToPreview(state: UserListState) { - UserListView(state = state) -} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchMultipleUsersResultItem.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchMultipleUsersResultItem.kt new file mode 100644 index 0000000000..7267af1f9b --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchMultipleUsersResultItem.kt @@ -0,0 +1,61 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.matrix.ui.components.CheckableMatrixUserRow +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.model.MatrixUser + +@Composable +fun SearchMultipleUsersResultItem( + matrixUser: MatrixUser, + isUserSelected: Boolean, + modifier: Modifier = Modifier, + onCheckedChange: (Boolean) -> Unit = {}, +) { + CheckableMatrixUserRow( + checked = isUserSelected, + modifier = modifier, + matrixUser = matrixUser, + avatarSize = AvatarSize.Custom(36.dp), + onCheckedChange = onCheckedChange, + ) +} + +@Preview +@Composable +internal fun SearchMultipleUsersResultItemLightPreview() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun SearchMultipleUsersResultItemDarkPreview() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + Column { + SearchMultipleUsersResultItem(matrixUser = aMatrixUser(), isUserSelected = true) + SearchMultipleUsersResultItem(matrixUser = aMatrixUser(), isUserSelected = false) + } +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchSingleUserResultItem.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchSingleUserResultItem.kt new file mode 100644 index 0000000000..67af583473 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchSingleUserResultItem.kt @@ -0,0 +1,55 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.clickable +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.model.MatrixUser + +@Composable +fun SearchSingleUserResultItem( + matrixUser: MatrixUser, + modifier: Modifier = Modifier, + onClick: () -> Unit = {}, +) { + MatrixUserRow( + modifier = modifier.clickable(onClick = onClick), + matrixUser = matrixUser, + avatarSize = AvatarSize.Custom(36.dp), + ) +} + +@Preview +@Composable +internal fun SearchSingleUserResultItemLightPreview() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun SearchSingleUserResultItemDarkPreview() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + SearchSingleUserResultItem(matrixUser = aMatrixUser()) +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt new file mode 100644 index 0000000000..83aad4151b --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt @@ -0,0 +1,146 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SearchBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +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.unit.dp +import io.element.android.libraries.designsystem.components.button.BackButton +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.SearchBar +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.ui.model.MatrixUser +import io.element.android.libraries.ui.strings.R +import kotlinx.collections.immutable.ImmutableList + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SearchUserBar( + query: String, + results: ImmutableList, + selectedUsers: ImmutableList, + selectedUsersListState: LazyListState, + active: Boolean, + isMultiSelectionEnabled: Boolean, + modifier: Modifier = Modifier, + placeHolderTitle: String = stringResource(R.string.common_search_for_someone), + onActiveChanged: (Boolean) -> Unit = {}, + onTextChanged: (String) -> Unit = {}, + onUserSelected: (MatrixUser) -> Unit = {}, + onUserDeselected: (MatrixUser) -> Unit = {}, +) { + val focusManager = LocalFocusManager.current + + if (!active) { + onTextChanged("") + focusManager.clearFocus() + } + + SearchBar( + query = query, + onQueryChange = onTextChanged, + onSearch = { focusManager.clearFocus() }, + active = active, + onActiveChange = onActiveChanged, + modifier = modifier + .padding(horizontal = if (!active) 16.dp else 0.dp), + placeholder = { + Text( + text = placeHolderTitle, + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + }, + leadingIcon = if (active) { + { BackButton(onClick = { onActiveChanged(false) }) } + } else { + null + }, + trailingIcon = when { + active && query.isNotEmpty() -> { + { + IconButton(onClick = { onTextChanged("") }) { + Icon(Icons.Default.Close, stringResource(R.string.action_clear)) + } + } + } + !active -> { + { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(R.string.action_search), + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + } + } + else -> null + }, + colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), + content = { + if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { + SelectedUsersList( + listState = selectedUsersListState, + contentPadding = PaddingValues(16.dp), + selectedUsers = selectedUsers, + onUserRemoved = onUserDeselected, + ) + } + + LazyColumn { + if (isMultiSelectionEnabled) { + items(results) { matrixUser -> + SearchMultipleUsersResultItem( + modifier = Modifier.fillMaxWidth(), + matrixUser = matrixUser, + isUserSelected = selectedUsers.find { it.id == matrixUser.id } != null, + onCheckedChange = { checked -> + if (checked) { + onUserSelected(matrixUser) + } else { + onUserDeselected(matrixUser) + } + } + ) + } + } else { + items(results) { matrixUser -> + SearchSingleUserResultItem( + modifier = Modifier.fillMaxWidth(), + matrixUser = matrixUser, + onClick = { onUserSelected(matrixUser) } + ) + } + } + } + }, + ) +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUser.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUser.kt new file mode 100644 index 0000000000..666a0c5265 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUser.kt @@ -0,0 +1,94 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.model.MatrixUser +import io.element.android.libraries.matrix.ui.model.getBestName +import io.element.android.libraries.ui.strings.R + +@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 = R.string.action_remove), + tint = MaterialTheme.colorScheme.onPrimary, + ) + } + } +} + +@Preview +@Composable +internal fun SelectedUserLightPreview() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun SelectedUserDarkPreview() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + SelectedUser(aMatrixUser()) +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt new file mode 100644 index 0000000000..4c2a0b10d3 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt @@ -0,0 +1,71 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.features.userlist.api.aListOfSelectedUsers +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun SelectedUsersList( + listState: LazyListState, + selectedUsers: ImmutableList, + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(0.dp), + onUserRemoved: (MatrixUser) -> Unit = {}, +) { + LazyRow( + state = listState, + modifier = modifier, + contentPadding = contentPadding, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + items(selectedUsers.toList()) { matrixUser -> + SelectedUser( + matrixUser = matrixUser, + onUserRemoved = onUserRemoved, + ) + } + } +} + +@Preview +@Composable +internal fun SelectedUsersListLightPreview() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun SelectedUsersListDarkPreview() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + SelectedUsersList( + listState = LazyListState(), + selectedUsers = aListOfSelectedUsers(), + ) +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt new file mode 100644 index 0000000000..42b96ab3a2 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt @@ -0,0 +1,91 @@ +/* + * 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.userlist.api.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.features.userlist.api.UserListEvents +import io.element.android.features.userlist.api.UserListState +import io.element.android.features.userlist.api.UserListStateProvider +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.matrix.ui.model.MatrixUser + +@Composable +fun UserListView( + state: UserListState, + modifier: Modifier = Modifier, + onUserSelected: (MatrixUser) -> Unit = {}, + onUserDeselected: (MatrixUser) -> Unit = {}, +) { + Column( + modifier = modifier, + ) { + SearchUserBar( + modifier = Modifier.fillMaxWidth(), + query = state.searchQuery, + results = state.searchResults, + selectedUsers = state.selectedUsers, + selectedUsersListState = state.selectedUsersListState, + active = state.isSearchActive, + isMultiSelectionEnabled = state.isMultiSelectionEnabled, + onActiveChanged = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) }, + onTextChanged = { state.eventSink(UserListEvents.UpdateSearchQuery(it)) }, + onUserSelected = { + state.eventSink(UserListEvents.AddToSelection(it)) + onUserSelected(it) + }, + onUserDeselected = { + state.eventSink(UserListEvents.RemoveFromSelection(it)) + onUserDeselected(it) + }, + ) + + if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { + SelectedUsersList( + listState = state.selectedUsersListState, + contentPadding = PaddingValues(16.dp), + selectedUsers = state.selectedUsers, + onUserRemoved = { + state.eventSink(UserListEvents.RemoveFromSelection(it)) + onUserDeselected(it) + }, + ) + } + } +} + +@Preview +@Composable +internal fun UserListViewLightPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun UserListViewDarkPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: UserListState) { + UserListView(state = state) +} From 97a40b35df20315ba87f4f1f09b2de997fcf6d5a Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 12 Apr 2023 16:48:00 +0200 Subject: [PATCH 05/24] Update screenshots --- .../impl/configureroom/ConfigureRoomStateProvider.kt | 10 ++++++++++ .../features/userlist/api/UserListStateProvider.kt | 2 +- .../components/avatar/AvatarDataProvider.kt | 6 +++--- .../matrix/ui/components/MatrixUserProvider.kt | 2 +- ...dPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...dPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...PeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...PeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...reRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...reRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ 12 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index b1614381ae..32744178dd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -18,11 +18,21 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.createroom.impl.CreateRoomConfig +import io.element.android.features.userlist.api.aListOfSelectedUsers open class ConfigureRoomStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aConfigureRoomState(), + aConfigureRoomState().copy( + config = CreateRoomConfig( + roomName = "Room 101", + topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", + invites = aListOfSelectedUsers(), + privacy = RoomPrivacy.Private, + ), + isCreateButtonEnabled = true, + ), ) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt index 80207fb4bc..8d3bf68f0d 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt @@ -63,4 +63,4 @@ fun aUserListState() = UserListState( eventSink = {} ) -fun aListOfSelectedUsers() = aMatrixUserList().take(4).toImmutableList() +fun aListOfSelectedUsers() = aMatrixUserList().take(6).toImmutableList() diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt index 5e4bad531c..61c76d80c3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt @@ -27,8 +27,8 @@ open class AvatarDataProvider : PreviewParameterProvider { ) } -fun anAvatarData() = AvatarData( +fun anAvatarData(id: String = "@id_of_alice:server.org", name: String = "Alice") = AvatarData( // Let's the id not start with a 'a'. - id = "@id_of_alice:server.org", - name = "Alice", + id = id, + name = name, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt index 89a8b4d5fd..e79e78802a 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt @@ -35,7 +35,7 @@ open class MatrixUserProvider : PreviewParameterProvider { fun aMatrixUser(id: String = "@id_of_alice:server.org", userName: String = "Alice") = MatrixUser( id = UserId(id), username = userName, - avatarData = anAvatarData() + avatarData = anAvatarData(id, userName) ) fun aMatrixUserList() = listOf( diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index ad94119f4c..596f01f6a6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:489aa166bf6d0f283da497b2752e457f81620959d404b5b9c3d96c7e18e8b953 -size 28100 +oid sha256:b96cf938377ee0f02e0cf2d5896eb83935d9416a5b87849ed81caed4db5e90f2 +size 41264 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 85c608618b..3fa12e378f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a8969c9a82f41bbdbdfb013412d039f3be4fcf450a0dd55f3217c64d51c8489 -size 23495 +oid sha256:d5fa485b200f71d2809efb3495b33f9cf000145d07cb5a37953d5f44057044c3 +size 35361 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 2074516963..339b2adf16 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5f29443193cd21eeb721a4c86f9063466439dd5ad4830708e6fa587f95e5abd -size 27456 +oid sha256:92fe30f0927b8be4ffc384548e6731e7d5e347dc5caa1c84251f2a932ee1873c +size 38848 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 2139d8a6c5..ef68b1d3d5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa79c54d2431dc0c8399a2bcb8e03d73749e9d54f505ac893e2376fded0e7dfe -size 22599 +oid sha256:0520170acaa265e514120f7552a551f660e6c56e6c5af7155c8d639a94eb2673 +size 33046 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 22e0b97b81..699b8973c1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2327de2be62afdacae32b297347fbcd23cd5e6986f513d018068aa981ecc3941 -size 89757 +oid sha256:756d835de7820c96019995ab47d34c2663330c4ddc9ca124eac266ea9eeef0ab +size 64397 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c273abf7fb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32e702b0c3671c6ef9ae38acadccf8c3da48b7093ac81ede95f8ea6f9b28e405 +size 103375 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 54b8ae3f9e..a9a3720ece 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50129ad0c4d75ff5e7b8ffd43b0ffdc40d77a78f5699f9b7194a9c9ef026af69 -size 82189 +oid sha256:a03aa11c787804adc5b2947359421b90e6abcdc7e840983a0276a6e02da59a2f +size 58526 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8693cd2e06 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad0d51590e0c66e5711d1a1809a234328717548dc7fac837dc4bce4870caf70a +size 96969 From 34515e4b493c0f8060c3a23eb410cff9123e99d8 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 12 Apr 2023 17:19:39 +0200 Subject: [PATCH 06/24] Rename MatrixUserDataSource to UserListDataSource --- .../createroom/impl/AllMatrixUsersDataSource.kt | 4 ++-- .../createroom/impl/addpeople/AddPeoplePresenter.kt | 6 +++--- .../features/createroom/impl/di/CreateRoomModule.kt | 4 ++-- .../createroom/impl/root/CreateRoomRootPresenter.kt | 6 +++--- .../impl/addpeople/AddPeoplePresenterTests.kt | 4 ++-- .../impl/root/CreateRoomRootPresenterTests.kt | 6 +++--- .../features/roomdetails/impl/di/RoomMemberModule.kt | 6 +++--- .../impl/members/RoomMemberListPresenter.kt | 8 ++++---- ...trixUserDataSource.kt => RoomUserListDataSource.kt} | 6 +++--- .../members/RoomMemberListPresenterTests.kt | 8 ++++---- .../{MatrixUserDataSource.kt => UserListDataSource.kt} | 2 +- .../android/features/userlist/api/UserListPresenter.kt | 2 +- .../features/userlist/impl/DefaultUserListPresenter.kt | 10 +++++----- .../userlist/impl/DefaultUserListPresenterTests.kt | 6 +++--- ...trixUserDataSource.kt => FakeUserListDataSource.kt} | 4 ++-- .../userlist/test/FakeUserListPresenterFactory.kt | 4 ++-- 16 files changed, 43 insertions(+), 43 deletions(-) rename features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/{RoomMatrixUserDataSource.kt => RoomUserListDataSource.kt} (92%) rename features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/{MatrixUserDataSource.kt => UserListDataSource.kt} (96%) rename features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/{FakeMatrixUserDataSource.kt => FakeUserListDataSource.kt} (90%) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt index 6bbdfb5e93..87f465ecbb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt @@ -16,13 +16,13 @@ package io.element.android.features.createroom.impl -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource 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 { +class AllMatrixUsersDataSource @Inject constructor() : UserListDataSource { override suspend fun search(query: String): List { return emptyList() } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index 7090a642af..6602e4de14 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -21,8 +21,8 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore -import io.element.android.features.userlist.api.MatrixUserDataSource import io.element.android.features.userlist.api.SelectionMode +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Presenter @@ -31,14 +31,14 @@ import javax.inject.Named class AddPeoplePresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, - @Named("AllUsers") private val matrixUserDataSource: MatrixUserDataSource, + @Named("AllUsers") private val userListDataSource: UserListDataSource, private val dataStore: CreateRoomDataStore, ) : Presenter { private val userListPresenter by lazy { userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Multiple), - matrixUserDataSource, + userListDataSource, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomModule.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomModule.kt index c5f2d0ca06..46c471bf1b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomModule.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/di/CreateRoomModule.kt @@ -20,7 +20,7 @@ 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.features.userlist.api.UserListDataSource import io.element.android.libraries.di.AppScope import javax.inject.Named @@ -30,6 +30,6 @@ interface CreateRoomModule { @Binds @Named("AllUsers") - fun bindAllUserListDataSource(dataSource: AllMatrixUsersDataSource): MatrixUserDataSource + fun bindAllUserListDataSource(dataSource: AllMatrixUsersDataSource): UserListDataSource } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt index 89932b0cdb..19ad6fdeb9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt @@ -21,8 +21,8 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import io.element.android.features.userlist.api.MatrixUserDataSource import io.element.android.features.userlist.api.SelectionMode +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -38,14 +38,14 @@ import javax.inject.Named class CreateRoomRootPresenter @Inject constructor( private val presenterFactory: UserListPresenter.Factory, - @Named("AllUsers") private val matrixUserDataSource: MatrixUserDataSource, + @Named("AllUsers") private val userListDataSource: UserListDataSource, private val matrixClient: MatrixClient, ) : Presenter { private val presenter by lazy { presenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), - matrixUserDataSource, + userListDataSource, ) } diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt index 086b5edf30..14dd92abce 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -22,7 +22,7 @@ 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.userlist.test.FakeMatrixUserDataSource +import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenterFactory import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -35,7 +35,7 @@ class AddPeoplePresenterTests { @Before fun setup() { - presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeMatrixUserDataSource()) + presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeUserListDataSource()) } @Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index cf399fdbd3..ea349f7128 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -23,7 +23,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.userlist.api.aUserListState -import io.element.android.features.userlist.test.FakeMatrixUserDataSource +import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenter import io.element.android.features.userlist.test.FakeUserListPresenterFactory import io.element.android.libraries.architecture.Async @@ -41,7 +41,7 @@ import org.junit.Test class CreateRoomRootPresenterTests { - private lateinit var userListDataSource: FakeMatrixUserDataSource + private lateinit var userListDataSource: FakeUserListDataSource private lateinit var presenter: CreateRoomRootPresenter private lateinit var fakeUserListPresenter: FakeUserListPresenter private lateinit var fakeMatrixClient: FakeMatrixClient @@ -50,7 +50,7 @@ class CreateRoomRootPresenterTests { fun setup() { fakeUserListPresenter = FakeUserListPresenter() fakeMatrixClient = FakeMatrixClient() - userListDataSource = FakeMatrixUserDataSource() + userListDataSource = FakeUserListDataSource() presenter = CreateRoomRootPresenter(FakeUserListPresenterFactory(fakeUserListPresenter), userListDataSource, fakeMatrixClient) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index 49c98374f2..88edd75f3e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -19,8 +19,8 @@ package io.element.android.features.roomdetails.impl.di import com.squareup.anvil.annotations.ContributesTo import dagger.Binds import dagger.Module -import io.element.android.features.roomdetails.impl.members.RoomMatrixUserDataSource -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.roomdetails.impl.members.RoomUserListDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.libraries.di.RoomScope import javax.inject.Named @@ -30,6 +30,6 @@ interface RoomMemberModule { @Binds @Named("RoomMembers") - fun bindRoomMemberUserListDataSource(dataSource: RoomMatrixUserDataSource): MatrixUserDataSource + fun bindRoomMemberUserListDataSource(dataSource: RoomUserListDataSource): UserListDataSource } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 8c3a873ade..24fb25991b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -21,7 +21,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -36,13 +36,13 @@ import javax.inject.Named class RoomMemberListPresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, - @Named("RoomMembers") private val matrixUserDataSource: MatrixUserDataSource, + @Named("RoomMembers") private val userListDataSource: UserListDataSource, ) : Presenter { private val userListPresenter by lazy { userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), - matrixUserDataSource, + userListDataSource, ) } @@ -52,7 +52,7 @@ class RoomMemberListPresenter @Inject constructor( val allUsers = remember { mutableStateOf>>(Async.Loading()) } LaunchedEffect(Unit) { withContext(Dispatchers.IO) { - allUsers.value = Async.Success(matrixUserDataSource.search("").toImmutableList()) + allUsers.value = Async.Success(userListDataSource.search("").toImmutableList()) } } return RoomMemberListState( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMatrixUserDataSource.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt similarity index 92% rename from features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMatrixUserDataSource.kt rename to features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt index b97dcd62e5..9f22c41666 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMatrixUserDataSource.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomUserListDataSource.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomdetails.impl.members -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId @@ -25,9 +25,9 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.ui.model.MatrixUser import javax.inject.Inject -class RoomMatrixUserDataSource @Inject constructor( +class RoomUserListDataSource @Inject constructor( private val room: MatrixRoom -) : MatrixUserDataSource { +) : UserListDataSource { override suspend fun search(query: String): List { return room.members().filter { member -> diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index 3564daa5f1..853d2eb613 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -22,11 +22,11 @@ import app.cash.turbine.test import com.google.common.truth.Truth import io.element.android.features.roomdetails.impl.members.RoomMemberListPresenter import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListPresenter 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.features.userlist.test.FakeUserListDataSource import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.ui.components.aMatrixUser import kotlinx.coroutines.test.runTest @@ -38,11 +38,11 @@ class RoomMemberListPresenterTests { @Test fun `present - search is done automatically on start, but is async`() = runTest { val searchResult = listOf(aMatrixUser()) - val userListDataSource = FakeMatrixUserDataSource().apply { + val userListDataSource = FakeUserListDataSource().apply { givenSearchResult(searchResult) } val userListFactory = object : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, dataSource: MatrixUserDataSource) = DefaultUserListPresenter(args, dataSource) + override fun create(args: UserListPresenterArgs, dataSource: UserListDataSource) = DefaultUserListPresenter(args, dataSource) } val presenter = RoomMemberListPresenter(userListFactory, userListDataSource) moleculeFlow(RecompositionClock.Immediate) { diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/MatrixUserDataSource.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataSource.kt similarity index 96% rename from features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/MatrixUserDataSource.kt rename to features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataSource.kt index 08eddfd7e9..afe2d1ab3d 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/MatrixUserDataSource.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataSource.kt @@ -19,7 +19,7 @@ package io.element.android.features.userlist.api import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser -interface MatrixUserDataSource { +interface UserListDataSource { suspend fun search(query: String): List suspend fun getProfile(userId: UserId): MatrixUser? } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt index c328efd44e..2fa416bfb5 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt @@ -21,6 +21,6 @@ import io.element.android.libraries.architecture.Presenter interface UserListPresenter : Presenter { interface Factory { - fun create(args: UserListPresenterArgs, matrixUserDataSource: MatrixUserDataSource): UserListPresenter + fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): UserListPresenter } } diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 567d183e15..5aaa106eb7 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -31,7 +31,7 @@ import com.squareup.anvil.annotations.ContributesBinding import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListEvents import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.api.UserListState @@ -48,13 +48,13 @@ import kotlinx.coroutines.launch class DefaultUserListPresenter @AssistedInject constructor( @Assisted val args: UserListPresenterArgs, - @Assisted val matrixUserDataSource: MatrixUserDataSource, + @Assisted val userListDataSource: UserListDataSource, ) : UserListPresenter { @AssistedFactory @ContributesBinding(SessionScope::class) interface DefaultUserListFactory : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, matrixUserDataSource: MatrixUserDataSource): DefaultUserListPresenter + override fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): DefaultUserListPresenter } @Composable @@ -110,9 +110,9 @@ class DefaultUserListPresenter @AssistedInject constructor( private suspend fun performSearch(query: String): ImmutableList { val isMatrixId = MatrixPatterns.isUserId(query) - val results = matrixUserDataSource.search(query).toMutableList() + val results = userListDataSource.search(query).toMutableList() if (isMatrixId && results.none { it.id.value == query }) { - val getProfileResult: MatrixUser? = matrixUserDataSource.getProfile(UserId(query)) + val getProfileResult: MatrixUser? = userListDataSource.getProfile(UserId(query)) val profile = getProfileResult ?: MatrixUser(UserId(query)) results.add(0, profile) } diff --git a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt index 1cae186d56..15c26fb8d3 100644 --- a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt +++ b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt @@ -21,10 +21,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.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListEvents import io.element.android.features.userlist.api.UserListPresenterArgs -import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.test.FakeMatrixUserDataSource +import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -37,7 +37,7 @@ import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) class DefaultUserListPresenterTests { - private val userListDataSource = FakeMatrixUserDataSource() + private val userListDataSource = FakeUserListDataSource() @Test fun `present - initial state for single selection`() = runTest { diff --git a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeMatrixUserDataSource.kt b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListDataSource.kt similarity index 90% rename from features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeMatrixUserDataSource.kt rename to features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListDataSource.kt index db6297ec05..ba0ccd2c89 100644 --- a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeMatrixUserDataSource.kt +++ b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListDataSource.kt @@ -16,11 +16,11 @@ package io.element.android.features.userlist.test -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser -class FakeMatrixUserDataSource : MatrixUserDataSource { +class FakeUserListDataSource : UserListDataSource { private var searchResult: List = emptyList() private var profile: MatrixUser? = null diff --git a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt index 37d50c303c..2255512490 100644 --- a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt +++ b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt @@ -16,7 +16,7 @@ package io.element.android.features.userlist.test -import io.element.android.features.userlist.api.MatrixUserDataSource +import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs @@ -24,5 +24,5 @@ class FakeUserListPresenterFactory( private val fakeUserListPresenter: FakeUserListPresenter = FakeUserListPresenter() ) : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, matrixUserDataSource: MatrixUserDataSource): UserListPresenter = fakeUserListPresenter + override fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): UserListPresenter = fakeUserListPresenter } From cc13629f96dd5edfaf6dc7ed06245baeb1ca97f4 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 12 Apr 2023 17:25:44 +0200 Subject: [PATCH 07/24] Fix tests --- .../configureroom/ConfigureRoomPresenter.kt | 21 +++++++++-------- .../impl/addpeople/AddPeoplePresenterTests.kt | 3 ++- .../ConfigureRoomPresenterTests.kt | 23 ++++++++++--------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 53523449f7..7cb5401511 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -41,17 +41,18 @@ class ConfigureRoomPresenter @Inject constructor( fun handleEvents(event: ConfigureRoomEvents) { when (event) { - is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(avatarUrl = event.uri?.toString())) - is ConfigureRoomEvents.RoomNameChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name)) - is ConfigureRoomEvents.TopicChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) - is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) - is ConfigureRoomEvents.RemoveFromSelection -> dataStore.setCreateRoomConfig( - createRoomConfig.value.copy( - invites = createRoomConfig.value.invites.minus( - event.matrixUser - ).toImmutableList() + is ConfigureRoomEvents.AvatarUriChanged -> + dataStore.setCreateRoomConfig(createRoomConfig.value.copy(avatarUrl = event.uri?.toString())) + is ConfigureRoomEvents.RoomNameChanged -> + dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name.takeUnless { it.isEmpty() })) + is ConfigureRoomEvents.TopicChanged -> + dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) + is ConfigureRoomEvents.RoomPrivacyChanged -> + dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) + is ConfigureRoomEvents.RemoveFromSelection -> + dataStore.setCreateRoomConfig( + createRoomConfig.value.copy(invites = createRoomConfig.value.invites.minus(event.matrixUser).toImmutableList()) ) - ) ConfigureRoomEvents.CreateRoom -> Unit } } diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt index 14dd92abce..4b89eb7cbc 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -22,6 +22,7 @@ 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.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenterFactory import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,7 +36,7 @@ class AddPeoplePresenterTests { @Before fun setup() { - presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeUserListDataSource()) + presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeUserListDataSource(), CreateRoomDataStore()) } @Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 0934909460..c77416bacb 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -23,6 +23,7 @@ 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.createroom.impl.CreateRoomDataStore import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_NAME @@ -40,7 +41,7 @@ class ConfigureRoomPresenterTests { @Before fun setup() { - presenter = ConfigureRoomPresenter(ConfigureRoomPresenterArgs(emptyList())) + presenter = ConfigureRoomPresenter(CreateRoomDataStore()) } @Test @@ -49,9 +50,9 @@ class ConfigureRoomPresenterTests { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.roomName).isEmpty() - assertThat(initialState.topic).isEmpty() - assertThat(initialState.privacy).isNull() + assertThat(initialState.config.roomName).isNull() + assertThat(initialState.config.topic).isNull() + assertThat(initialState.config.privacy).isNull() } } @@ -66,19 +67,19 @@ class ConfigureRoomPresenterTests { // Room name not empty initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() - assertThat(newState.roomName).isEqualTo(A_ROOM_NAME) + assertThat(newState.config.roomName).isEqualTo(A_ROOM_NAME) assertThat(newState.isCreateButtonEnabled).isFalse() // Select privacy initialState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Private)) newState = awaitItem() - assertThat(newState.privacy).isEqualTo(RoomPrivacy.Private) + assertThat(newState.config.privacy).isEqualTo(RoomPrivacy.Private) assertThat(newState.isCreateButtonEnabled).isTrue() // Clear room name initialState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) newState = awaitItem() - assertThat(newState.roomName).isEqualTo("") + assertThat(newState.config.roomName).isNull() assertThat(newState.isCreateButtonEnabled).isFalse() } } @@ -92,23 +93,23 @@ class ConfigureRoomPresenterTests { // Room name initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) val stateAfterRoomNameChanged = awaitItem() - assertThat(stateAfterRoomNameChanged.roomName).isEqualTo(A_ROOM_NAME) + assertThat(stateAfterRoomNameChanged.config.roomName).isEqualTo(A_ROOM_NAME) // Room topic stateAfterRoomNameChanged.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE)) val stateAfterTopicChanged = awaitItem() - assertThat(stateAfterTopicChanged.topic).isEqualTo(A_MESSAGE) + assertThat(stateAfterTopicChanged.config.topic).isEqualTo(A_MESSAGE) // Room avatar val anUri = Uri.parse(AN_AVATAR_URL) stateAfterTopicChanged.eventSink(ConfigureRoomEvents.AvatarUriChanged(anUri)) val stateAfterAvatarUriChanged = awaitItem() - assertThat(stateAfterAvatarUriChanged.avatarUri).isEqualTo(anUri) + assertThat(stateAfterAvatarUriChanged.config.avatarUrl).isEqualTo(anUri.toString()) // Room privacy stateAfterAvatarUriChanged.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) val stateAfterPrivacyChanged = awaitItem() - assertThat(stateAfterPrivacyChanged.privacy).isEqualTo(RoomPrivacy.Public) + assertThat(stateAfterPrivacyChanged.config.privacy).isEqualTo(RoomPrivacy.Public) } } } From 6a8179a5b5807795431eb491a3fa5a1ec956c1e6 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 10:06:05 +0200 Subject: [PATCH 08/24] Persist selected users in data store --- .../createroom/impl/CreateRoomDataStore.kt | 31 ++++++++++++++-- .../impl/addpeople/AddPeoplePresenter.kt | 8 +--- .../configureroom/ConfigureRoomPresenter.kt | 18 +++------ .../impl/root/CreateRoomRootPresenter.kt | 3 ++ .../impl/members/RoomMemberListPresenter.kt | 3 ++ .../userlist/api/UserListDataStore.kt | 37 +++++++++++++++++++ .../userlist/api/UserListPresenter.kt | 6 ++- .../userlist/impl/DefaultUserListPresenter.kt | 19 ++++++---- 8 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index ed61431511..e55fca755d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -16,20 +16,43 @@ package io.element.android.features.createroom.impl +import io.element.android.features.createroom.impl.configureroom.RoomPrivacy import io.element.android.features.createroom.impl.di.CreateRoomScope +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.libraries.di.SingleIn +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import javax.inject.Inject @SingleIn(CreateRoomScope::class) -class CreateRoomDataStore @Inject constructor() { +class CreateRoomDataStore @Inject constructor( + val selectedUserListDataStore: UserListDataStore, +) { private val createRoomConfigFlow: MutableStateFlow = MutableStateFlow(CreateRoomConfig()) - fun getCreateRoomConfig(): Flow = createRoomConfigFlow + fun getCreateRoomConfig(): Flow = combine( + selectedUserListDataStore.selectedUsers(), + createRoomConfigFlow, + ) { selectedUsers, config -> + config.copy(invites = selectedUsers.toImmutableList()) + } - fun setCreateRoomConfig(createRoomConfig: CreateRoomConfig) { - createRoomConfigFlow.tryEmit(createRoomConfig) + fun setRoomName(roomName: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(roomName = roomName?.takeIf { it.isNotEmpty() })) + } + + fun setTopic(topic: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(topic = topic?.takeIf { it.isNotEmpty() })) + } + + fun setAvatarUrl(avatarUrl: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(avatarUrl = avatarUrl)) + } + + fun setPrivacy(privacy: RoomPrivacy?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(privacy = privacy)) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index 6602e4de14..88e24f58b7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -17,9 +17,6 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource @@ -39,16 +36,13 @@ class AddPeoplePresenter @Inject constructor( userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Multiple), userListDataSource, + dataStore.selectedUserListDataStore, ) } @Composable override fun present(): AddPeopleState { val userListState = userListPresenter.present() - val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) - LaunchedEffect(userListState.selectedUsers) { - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(invites = userListState.selectedUsers)) - } fun handleEvents(event: AddPeopleEvents) { // do nothing for now } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 7cb5401511..e9211976af 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter -import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -41,18 +40,11 @@ class ConfigureRoomPresenter @Inject constructor( fun handleEvents(event: ConfigureRoomEvents) { when (event) { - is ConfigureRoomEvents.AvatarUriChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(avatarUrl = event.uri?.toString())) - is ConfigureRoomEvents.RoomNameChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name.takeUnless { it.isEmpty() })) - is ConfigureRoomEvents.TopicChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) - is ConfigureRoomEvents.RoomPrivacyChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) - is ConfigureRoomEvents.RemoveFromSelection -> - dataStore.setCreateRoomConfig( - createRoomConfig.value.copy(invites = createRoomConfig.value.invites.minus(event.matrixUser).toImmutableList()) - ) + is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setAvatarUrl(event.uri?.toString()) + is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) + is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) + is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) + is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) ConfigureRoomEvents.CreateRoom -> Unit } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt index 19ad6fdeb9..2ef30908e1 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -39,6 +40,7 @@ import javax.inject.Named class CreateRoomRootPresenter @Inject constructor( private val presenterFactory: UserListPresenter.Factory, @Named("AllUsers") private val userListDataSource: UserListDataSource, + private val userListDataStore: UserListDataStore, private val matrixClient: MatrixClient, ) : Presenter { @@ -46,6 +48,7 @@ class CreateRoomRootPresenter @Inject constructor( presenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), userListDataSource, + userListDataStore, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 24fb25991b..8841bd9d5e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -37,12 +38,14 @@ import javax.inject.Named class RoomMemberListPresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, @Named("RoomMembers") private val userListDataSource: UserListDataSource, + private val userListDataStore: UserListDataStore, ) : Presenter { private val userListPresenter by lazy { userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), userListDataSource, + userListDataStore, ) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt new file mode 100644 index 0000000000..c1e982dd59 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt @@ -0,0 +1,37 @@ +/* + * 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.userlist.api + +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +class UserListDataStore @Inject constructor() { + + private val selectedUsers: MutableStateFlow> = MutableStateFlow(emptyList()) + + fun selectUser(user: MatrixUser) { + selectedUsers.tryEmit(selectedUsers.value.plus(user)) + } + + fun removeUserFromSelection(user: MatrixUser) { + selectedUsers.tryEmit(selectedUsers.value.minus(user)) + } + + fun selectedUsers(): Flow> = selectedUsers +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt index 2fa416bfb5..90205eab2a 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt @@ -21,6 +21,10 @@ import io.element.android.libraries.architecture.Presenter interface UserListPresenter : Presenter { interface Factory { - fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): UserListPresenter + fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ): UserListPresenter } } diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 5aaa106eb7..8e1829f4a7 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -32,10 +33,11 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListEvents +import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.api.UserListState -import io.element.android.features.userlist.api.UserListPresenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId @@ -49,21 +51,24 @@ import kotlinx.coroutines.launch class DefaultUserListPresenter @AssistedInject constructor( @Assisted val args: UserListPresenterArgs, @Assisted val userListDataSource: UserListDataSource, + @Assisted val userListDataStore: UserListDataStore, ) : UserListPresenter { @AssistedFactory @ContributesBinding(SessionScope::class) interface DefaultUserListFactory : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): DefaultUserListPresenter + override fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ): DefaultUserListPresenter } @Composable override fun present(): UserListState { val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers: MutableState> = remember { - mutableStateOf(persistentListOf()) - } + val selectedUsers = userListDataStore.selectedUsers().collectAsState(emptyList()) val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { @@ -76,11 +81,11 @@ class DefaultUserListPresenter @AssistedInject constructor( is UserListEvents.UpdateSearchQuery -> searchQuery = event.query is UserListEvents.AddToSelection -> { if (event.matrixUser !in selectedUsers.value) { - selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + userListDataStore.selectUser(event.matrixUser) } localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) } - is UserListEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) } } From 68c093a0df30a65c372ccdd47df34ced4bcd28fc Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 10:26:21 +0200 Subject: [PATCH 09/24] Improve AddPeople node --- .../impl/addpeople/AddPeopleEvents.kt | 19 ---------- .../impl/addpeople/AddPeoplePresenter.kt | 19 +++------- .../impl/addpeople/AddPeopleState.kt | 24 ------------- .../impl/addpeople/AddPeopleStateProvider.kt | 35 +++++++++---------- .../impl/addpeople/AddPeopleView.kt | 19 +++++----- 5 files changed, 29 insertions(+), 87 deletions(-) delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt delete mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt deleted file mode 100644 index 5d246be501..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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.addpeople - -sealed interface AddPeopleEvents diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index 88e24f58b7..21ab9fafbd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -18,10 +18,7 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable import io.element.android.features.createroom.impl.CreateRoomDataStore -import io.element.android.features.userlist.api.SelectionMode -import io.element.android.features.userlist.api.UserListDataSource -import io.element.android.features.userlist.api.UserListPresenter -import io.element.android.features.userlist.api.UserListPresenterArgs +import io.element.android.features.userlist.api.* import io.element.android.libraries.architecture.Presenter import javax.inject.Inject import javax.inject.Named @@ -30,7 +27,7 @@ class AddPeoplePresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, @Named("AllUsers") private val userListDataSource: UserListDataSource, private val dataStore: CreateRoomDataStore, -) : Presenter { +) : Presenter { private val userListPresenter by lazy { userListPresenterFactory.create( @@ -41,16 +38,8 @@ class AddPeoplePresenter @Inject constructor( } @Composable - override fun present(): AddPeopleState { - val userListState = userListPresenter.present() - fun handleEvents(event: AddPeopleEvents) { - // do nothing for now - } - - return AddPeopleState( - userListState = userListState, - eventSink = ::handleEvents, - ) + override fun present(): UserListState { + return userListPresenter.present() } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt deleted file mode 100644 index 8605e1aba6..0000000000 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.addpeople - -import io.element.android.features.userlist.api.UserListState - -data class AddPeopleState( - val userListState: UserListState, - val eventSink: (AddPeopleEvents) -> Unit, -) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt index cfbf7941ce..3c9073d0d0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt @@ -18,30 +18,27 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.userlist.api.SelectionMode +import io.element.android.features.userlist.api.UserListState import io.element.android.features.userlist.api.aListOfSelectedUsers import io.element.android.features.userlist.api.aUserListState +import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import kotlinx.collections.immutable.toImmutableList -open class AddPeopleStateProvider : PreviewParameterProvider { - override val values: Sequence +open class AddPeopleStateProvider : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( - aAddPeopleState(), - aAddPeopleState().copy( - userListState = aUserListState().copy( - selectedUsers = aListOfSelectedUsers(), - selectionMode = SelectionMode.Multiple, - ) + aUserListState(), + aUserListState().copy( + searchResults = aMatrixUserList().toImmutableList(), + selectedUsers = aListOfSelectedUsers(), + isSearchActive = false, + selectionMode = SelectionMode.Multiple, ), - aAddPeopleState().copy( - userListState = aUserListState().copy( - selectedUsers = aListOfSelectedUsers(), - isSearchActive = true, - selectionMode = SelectionMode.Multiple, - ) + aUserListState().copy( + searchResults = aMatrixUserList().toImmutableList(), + selectedUsers = aListOfSelectedUsers(), + isSearchActive = true, + selectionMode = SelectionMode.Multiple, ) ) } - -fun aAddPeopleState() = AddPeopleState( - userListState = aUserListState(), - eventSink = {} -) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index 9de3e0fa20..e3e7911a20 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.createroom.impl.R +import io.element.android.features.userlist.api.UserListState import io.element.android.features.userlist.api.components.UserListView import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -44,20 +45,18 @@ import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @Composable fun AddPeopleView( - state: AddPeopleState, + state: UserListState, modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, onNextPressed: (List) -> Unit = {}, ) { - val eventSink = state.eventSink - Scaffold( topBar = { - if (!state.userListState.isSearchActive) { + if (!state.isSearchActive) { AddPeopleViewTopBar( - hasSelectedUsers = state.userListState.selectedUsers.isNotEmpty(), + hasSelectedUsers = state.selectedUsers.isNotEmpty(), onBackPressed = onBackPressed, - onNextPressed = { onNextPressed(state.userListState.selectedUsers) }, + onNextPressed = { onNextPressed(state.selectedUsers) }, ) } } @@ -69,7 +68,7 @@ fun AddPeopleView( ) { UserListView( modifier = Modifier.fillMaxWidth(), - state = state.userListState, + state = state, ) } } @@ -110,15 +109,15 @@ fun AddPeopleViewTopBar( @Preview @Composable -internal fun AddPeopleViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = +internal fun AddPeopleViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: UserListState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun AddPeopleViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = +internal fun AddPeopleViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: UserListState) = ElementPreviewDark { ContentToPreview(state) } @Composable -private fun ContentToPreview(state: AddPeopleState) { +private fun ContentToPreview(state: UserListState) { AddPeopleView(state = state) } From 8d84011808d3d5bd46c0d36f5af6c124aa5717eb Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 11:34:19 +0200 Subject: [PATCH 10/24] Introduce ConfigureRoomFlowNode and bind CreateRoomScope to this flow --- .../createroom/impl/ConfigureRoomFlowNode.kt | 96 +++++++++++++++++++ .../createroom/impl/CreateRoomFlowNode.kt | 43 ++------- 2 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt new file mode 100644 index 0000000000..55c6c693c5 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt @@ -0,0 +1,96 @@ +/* + * 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 android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.composable.Children +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.navmodel.backstack.BackStack +import com.bumble.appyx.navmodel.backstack.operation.push +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.createroom.impl.addpeople.AddPeopleNode +import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode +import io.element.android.features.createroom.impl.di.CreateRoomComponent +import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.DaggerComponentOwner +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.parcelize.Parcelize + +@ContributesNode(SessionScope::class) +class ConfigureRoomFlowNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, +) : DaggerComponentOwner, + BackstackNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins + ) { + + private val component by lazy { + parent!!.bindings().createRoomComponentBuilder().build() + } + + override val daggerComponent: Any + get() = component + + sealed interface NavTarget : Parcelable { + @Parcelize + object Root : NavTarget + + @Parcelize + object ConfigureRoom : NavTarget + } + + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { + return when (navTarget) { + NavTarget.Root -> { + val callback = object : AddPeopleNode.Callback { + override fun onContinue(selectedUsers: List) { + backstack.push(NavTarget.ConfigureRoom) + } + } + createNode(context = buildContext, plugins = listOf(callback)) + } + NavTarget.ConfigureRoom -> { + createNode(context = buildContext) + } + } + } + + @Composable + override fun View(modifier: Modifier) { + Children( + navModel = backstack, + modifier = modifier, + transitionHandler = rememberDefaultTransitionHandler() + ) + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index c663814c19..6b9edc68d2 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -30,40 +30,26 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint -import io.element.android.features.createroom.impl.addpeople.AddPeopleNode -import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode -import io.element.android.features.createroom.impl.di.CreateRoomComponent import io.element.android.features.createroom.impl.root.CreateRoomRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler -import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) class CreateRoomFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, -) : DaggerComponentOwner, - BackstackNode( - backstack = BackStack( - initialElement = NavTarget.Root, - savedStateMap = buildContext.savedStateMap, - ), - buildContext = buildContext, - plugins = plugins - ) { - - private val component by lazy { - parent!!.bindings().createRoomComponentBuilder().build() - } - - override val daggerComponent: Any - get() = component +) : BackstackNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins +) { sealed interface NavTarget : Parcelable { @Parcelize @@ -71,9 +57,6 @@ class CreateRoomFlowNode @AssistedInject constructor( @Parcelize object NewRoom : NavTarget - - @Parcelize - object ConfigureRoom : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -91,15 +74,7 @@ class CreateRoomFlowNode @AssistedInject constructor( createNode(context = buildContext, plugins = listOf(callback)) } NavTarget.NewRoom -> { - val callback = object : AddPeopleNode.Callback { - override fun onContinue(selectedUsers: List) { - backstack.push(NavTarget.ConfigureRoom) - } - } - createNode(context = buildContext, plugins = listOf(callback)) - } - NavTarget.ConfigureRoom -> { - createNode(context = buildContext) + createNode(context = buildContext, plugins = emptyList()) } } } From 21670684105c7683e1049133a0314a7996c403f4 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 11:37:17 +0200 Subject: [PATCH 11/24] Remove useless selectedUsers parameter --- .../features/createroom/impl/ConfigureRoomFlowNode.kt | 3 +-- .../features/createroom/impl/addpeople/AddPeopleNode.kt | 9 ++++----- .../features/createroom/impl/addpeople/AddPeopleView.kt | 5 ++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt index 55c6c693c5..a01f0747f5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt @@ -37,7 +37,6 @@ import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) @@ -73,7 +72,7 @@ class ConfigureRoomFlowNode @AssistedInject constructor( return when (navTarget) { NavTarget.Root -> { val callback = object : AddPeopleNode.Callback { - override fun onContinue(selectedUsers: List) { + override fun onContinue() { backstack.push(NavTarget.ConfigureRoom) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index 2d060a6644..1b4bd9ac8d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -26,7 +26,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.impl.di.CreateRoomScope -import io.element.android.libraries.matrix.ui.model.MatrixUser @ContributesNode(CreateRoomScope::class) class AddPeopleNode @AssistedInject constructor( @@ -36,11 +35,11 @@ class AddPeopleNode @AssistedInject constructor( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onContinue(selectedUsers: List) + fun onContinue() } - private fun onContinue(selectedUsers: List) { - plugins().forEach { it.onContinue(selectedUsers) } + private fun onContinue() { + plugins().forEach { it.onContinue() } } @Composable @@ -49,7 +48,7 @@ class AddPeopleNode @AssistedInject constructor( AddPeopleView( state = state, modifier = modifier, - onBackPressed = { navigateUp() }, + onBackPressed = this::navigateUp, onNextPressed = this::onContinue, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index e3e7911a20..e81a47a04a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -39,7 +39,6 @@ import io.element.android.libraries.designsystem.theme.components.CenterAlignedT import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @@ -48,7 +47,7 @@ fun AddPeopleView( state: UserListState, modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, - onNextPressed: (List) -> Unit = {}, + onNextPressed: () -> Unit = {}, ) { Scaffold( topBar = { @@ -56,7 +55,7 @@ fun AddPeopleView( AddPeopleViewTopBar( hasSelectedUsers = state.selectedUsers.isNotEmpty(), onBackPressed = onBackPressed, - onNextPressed = { onNextPressed(state.selectedUsers) }, + onNextPressed = onNextPressed, ) } } From 063d3a66df53686ea041d39dd0042d396c63e617 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 14:12:46 +0200 Subject: [PATCH 12/24] Show toast for not implemented actions --- .../createroom/impl/configureroom/ConfigureRoomPresenter.kt | 5 ++++- .../createroom/impl/configureroom/ConfigureRoomView.kt | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index e9211976af..b504edae96 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -16,11 +16,13 @@ package io.element.android.features.createroom.impl.configureroom +import android.widget.Toast import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.platform.LocalContext import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter @@ -38,6 +40,7 @@ class ConfigureRoomPresenter @Inject constructor( mutableStateOf(enabled) } + val context = LocalContext.current fun handleEvents(event: ConfigureRoomEvents) { when (event) { is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setAvatarUrl(event.uri?.toString()) @@ -45,7 +48,7 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) - ConfigureRoomEvents.CreateRoom -> Unit + ConfigureRoomEvents.CreateRoom -> Toast.makeText(context, "not implemented yet", Toast.LENGTH_SHORT).show() } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index ddb000d883..b85901fc2b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -17,6 +17,7 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri +import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -77,6 +78,7 @@ fun ConfigureRoomView( onBackPressed: () -> Unit = {}, ) { val selectedUsersListState = rememberLazyListState() + val context = LocalContext.current Scaffold( modifier = modifier, topBar = { @@ -95,6 +97,7 @@ fun ConfigureRoomView( modifier = Modifier.padding(horizontal = 16.dp), avatarUri = state.config.avatarUrl?.toUri(), roomName = state.config.roomName.orEmpty(), + onAvatarClick = { Toast.makeText(context, "not implemented yet", Toast.LENGTH_SHORT).show() }, onRoomNameChanged = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, ) RoomTopic( From 39c05f7bed2bcdcca2c8eef936c5d74a543af59e Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 14:24:33 +0200 Subject: [PATCH 13/24] Update screenshots --- ...roup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...oup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 3fa12e378f..8b87a5a644 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5fa485b200f71d2809efb3495b33f9cf000145d07cb5a37953d5f44057044c3 -size 35361 +oid sha256:56148beb26b35c8309190271b43fc225e11b90b8b849a8e60abf98b6ab663c1b +size 101010 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index ef68b1d3d5..ba7682c859 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0520170acaa265e514120f7552a551f660e6c56e6c5af7155c8d639a94eb2673 -size 33046 +oid sha256:9fa60afaf7ab23f66622d7d77e168db8b9d8bc15d178e8adb63f944de3cc29df +size 96242 From 2ee536da3b757c87f12259fd1a7f631f0baddce4 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 15:44:00 +0200 Subject: [PATCH 14/24] do not reverse selected user list ordering & add autoscroll when selecting user --- .../impl/configureroom/ConfigureRoomView.kt | 3 --- .../features/userlist/api/UserListState.kt | 2 -- .../userlist/api/UserListStateProvider.kt | 5 ---- .../userlist/api/components/SearchUserBar.kt | 4 +-- .../api/components/SelectedUsersList.kt | 27 ++++++++++++++----- .../userlist/api/components/UserListView.kt | 3 +-- .../userlist/impl/DefaultUserListPresenter.kt | 15 +---------- 7 files changed, 24 insertions(+), 35 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index b85901fc2b..34d0332c0f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.shape.CircleShape @@ -77,7 +76,6 @@ fun ConfigureRoomView( modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, ) { - val selectedUsersListState = rememberLazyListState() val context = LocalContext.current Scaffold( modifier = modifier, @@ -106,7 +104,6 @@ fun ConfigureRoomView( onTopicChanged = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, ) SelectedUsersList( - listState = selectedUsersListState, contentPadding = PaddingValues(horizontal = 24.dp), selectedUsers = state.config.invites, onUserRemoved = { state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) }, diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt index 80de1e991f..dfbfddbcf5 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt @@ -16,7 +16,6 @@ package io.element.android.features.userlist.api -import androidx.compose.foundation.lazy.LazyListState import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList @@ -24,7 +23,6 @@ data class UserListState( val searchQuery: String, val searchResults: ImmutableList, val selectedUsers: ImmutableList, - val selectedUsersListState: LazyListState, val isSearchActive: Boolean, val selectionMode: SelectionMode, val eventSink: (UserListEvents) -> Unit, diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt index 8d3bf68f0d..7ea0ba0827 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt @@ -16,7 +16,6 @@ package io.element.android.features.userlist.api -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.ui.components.aMatrixUserList import kotlinx.collections.immutable.persistentListOf @@ -55,10 +54,6 @@ fun aUserListState() = UserListState( searchQuery = "", searchResults = persistentListOf(), selectedUsers = persistentListOf(), - selectedUsersListState = LazyListState( - firstVisibleItemIndex = 0, - firstVisibleItemScrollOffset = 0, - ), selectionMode = SelectionMode.Single, eventSink = {} ) diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt index 83aad4151b..baee8c5b2a 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -49,7 +48,6 @@ fun SearchUserBar( query: String, results: ImmutableList, selectedUsers: ImmutableList, - selectedUsersListState: LazyListState, active: Boolean, isMultiSelectionEnabled: Boolean, modifier: Modifier = Modifier, @@ -108,9 +106,9 @@ fun SearchUserBar( content = { if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { SelectedUsersList( - listState = selectedUsersListState, contentPadding = PaddingValues(16.dp), selectedUsers = selectedUsers, + autoScroll = true, onUserRemoved = onUserDeselected, ) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt index 4c2a0b10d3..491fb7827f 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt @@ -18,10 +18,15 @@ package io.element.android.features.userlist.api.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -29,18 +34,29 @@ import io.element.android.features.userlist.api.aListOfSelectedUsers import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList @Composable fun SelectedUsersList( - listState: LazyListState, - selectedUsers: ImmutableList, + selectedUsers: List, modifier: Modifier = Modifier, + autoScroll: Boolean = false, contentPadding: PaddingValues = PaddingValues(0.dp), onUserRemoved: (MatrixUser) -> Unit = {}, ) { + val lazyListState = rememberLazyListState() + if (autoScroll) { + var currentSize by rememberSaveable { mutableStateOf(selectedUsers.size) } + LaunchedEffect(selectedUsers.size) { + val isItemAdded = selectedUsers.size > currentSize + if (isItemAdded) { + lazyListState.animateScrollToItem(selectedUsers.lastIndex) + } + currentSize = selectedUsers.size + } + } + LazyRow( - state = listState, + state = lazyListState, modifier = modifier, contentPadding = contentPadding, horizontalArrangement = Arrangement.spacedBy(24.dp), @@ -65,7 +81,6 @@ internal fun SelectedUsersListDarkPreview() = ElementPreviewDark { ContentToPrev @Composable private fun ContentToPreview() { SelectedUsersList( - listState = LazyListState(), selectedUsers = aListOfSelectedUsers(), ) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt index 42b96ab3a2..6532dea2b6 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt @@ -46,7 +46,6 @@ fun UserListView( query = state.searchQuery, results = state.searchResults, selectedUsers = state.selectedUsers, - selectedUsersListState = state.selectedUsersListState, active = state.isSearchActive, isMultiSelectionEnabled = state.isMultiSelectionEnabled, onActiveChanged = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) }, @@ -63,9 +62,9 @@ fun UserListView( if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { SelectedUsersList( - listState = state.selectedUsersListState, contentPadding = PaddingValues(16.dp), selectedUsers = state.selectedUsers, + autoScroll = true, onUserRemoved = { state.eventSink(UserListEvents.RemoveFromSelection(it)) onUserDeselected(it) diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 8e1829f4a7..965bf40983 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -16,8 +16,6 @@ package io.element.android.features.userlist.impl -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState @@ -25,7 +23,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.squareup.anvil.annotations.ContributesBinding @@ -45,8 +42,6 @@ import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch class DefaultUserListPresenter @AssistedInject constructor( @Assisted val args: UserListPresenterArgs, @@ -66,10 +61,8 @@ class DefaultUserListPresenter @AssistedInject constructor( @Composable override fun present(): UserListState { - val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } val selectedUsers = userListDataStore.selectedUsers().collectAsState(emptyList()) - val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { mutableStateOf(persistentListOf()) @@ -83,7 +76,6 @@ class DefaultUserListPresenter @AssistedInject constructor( if (event.matrixUser !in selectedUsers.value) { userListDataStore.selectUser(event.matrixUser) } - localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) } is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) } @@ -105,8 +97,7 @@ class DefaultUserListPresenter @AssistedInject constructor( return UserListState( searchQuery = searchQuery, searchResults = searchResults.value, - selectedUsers = selectedUsers.value.reversed().toImmutableList(), - selectedUsersListState = selectedUsersListState, + selectedUsers = selectedUsers.value.toImmutableList(), isSearchActive = isSearchActive, selectionMode = args.selectionMode, eventSink = ::handleEvents, @@ -123,8 +114,4 @@ class DefaultUserListPresenter @AssistedInject constructor( } return results.toImmutableList() } - - private fun CoroutineScope.scrollToFirstSelectedUser(listState: LazyListState) = launch { - listState.scrollToItem(index = 0) - } } From 60e5c1c544e15d0b940c3311f62fcb58ec7f1a1e Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 16:06:21 +0200 Subject: [PATCH 15/24] Fix unit tests --- .../impl/configureroom/ConfigureRoomPresenter.kt | 5 +---- .../impl/configureroom/ConfigureRoomView.kt | 5 ++++- .../impl/addpeople/AddPeoplePresenterTests.kt | 3 ++- .../impl/configureroom/ConfigureRoomPresenterTests.kt | 3 ++- .../impl/root/CreateRoomRootPresenterTests.kt | 3 ++- .../members/RoomMemberListPresenterTests.kt | 11 ++++++++--- .../userlist/test/FakeUserListPresenterFactory.kt | 7 ++++++- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index b504edae96..ffee2e3fd9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -16,13 +16,11 @@ package io.element.android.features.createroom.impl.configureroom -import android.widget.Toast import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.platform.LocalContext import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter @@ -40,7 +38,6 @@ class ConfigureRoomPresenter @Inject constructor( mutableStateOf(enabled) } - val context = LocalContext.current fun handleEvents(event: ConfigureRoomEvents) { when (event) { is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setAvatarUrl(event.uri?.toString()) @@ -48,7 +45,7 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) - ConfigureRoomEvents.CreateRoom -> Toast.makeText(context, "not implemented yet", Toast.LENGTH_SHORT).show() + ConfigureRoomEvents.CreateRoom -> Unit // TODO } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 34d0332c0f..425143af7c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -83,7 +83,10 @@ fun ConfigureRoomView( ConfigureRoomToolbar( isNextActionEnabled = state.isCreateButtonEnabled, onBackPressed = onBackPressed, - onNextPressed = { state.eventSink(ConfigureRoomEvents.CreateRoom) }, + onNextPressed = { + // state.eventSink(ConfigureRoomEvents.CreateRoom) + Toast.makeText(context, "not implemented yet", Toast.LENGTH_SHORT).show() + }, ) } ) { padding -> diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt index 4b89eb7cbc..7ca2f0147f 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -23,6 +23,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.impl.CreateRoomDataStore +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenterFactory import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -36,7 +37,7 @@ class AddPeoplePresenterTests { @Before fun setup() { - presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeUserListDataSource(), CreateRoomDataStore()) + presenter = AddPeoplePresenter(FakeUserListPresenterFactory(), FakeUserListDataSource(), CreateRoomDataStore(UserListDataStore())) } @Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index c77416bacb..4828b7e636 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -24,6 +24,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.impl.CreateRoomDataStore +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_NAME @@ -41,7 +42,7 @@ class ConfigureRoomPresenterTests { @Before fun setup() { - presenter = ConfigureRoomPresenter(CreateRoomDataStore()) + presenter = ConfigureRoomPresenter(CreateRoomDataStore(UserListDataStore())) } @Test diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index ea349f7128..4d26e68bcb 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -22,6 +22,7 @@ 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.userlist.api.UserListDataStore import io.element.android.features.userlist.api.aUserListState import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.features.userlist.test.FakeUserListPresenter @@ -51,7 +52,7 @@ class CreateRoomRootPresenterTests { fakeUserListPresenter = FakeUserListPresenter() fakeMatrixClient = FakeMatrixClient() userListDataSource = FakeUserListDataSource() - presenter = CreateRoomRootPresenter(FakeUserListPresenterFactory(fakeUserListPresenter), userListDataSource, fakeMatrixClient) + presenter = CreateRoomRootPresenter(FakeUserListPresenterFactory(fakeUserListPresenter), userListDataSource, UserListDataStore(), fakeMatrixClient) } @Test diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index 853d2eb613..097eb46b30 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -23,6 +23,7 @@ import com.google.common.truth.Truth import io.element.android.features.roomdetails.impl.members.RoomMemberListPresenter import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.impl.DefaultUserListPresenter @@ -41,10 +42,15 @@ class RoomMemberListPresenterTests { val userListDataSource = FakeUserListDataSource().apply { givenSearchResult(searchResult) } + val userListDataStore = UserListDataStore() val userListFactory = object : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, dataSource: UserListDataSource) = DefaultUserListPresenter(args, dataSource) + override fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ) = DefaultUserListPresenter(args, userListDataSource, userListDataStore) } - val presenter = RoomMemberListPresenter(userListFactory, userListDataSource) + val presenter = RoomMemberListPresenter(userListFactory, userListDataSource, userListDataStore) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -58,5 +64,4 @@ class RoomMemberListPresenterTests { Truth.assertThat((loadedState.allUsers as? Async.Success)?.state).isEqualTo(searchResult.toImmutableList()) } } - } diff --git a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt index 2255512490..00966a5082 100644 --- a/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt +++ b/features/userlist/test/src/main/kotlin/io/element/android/features/userlist/test/FakeUserListPresenterFactory.kt @@ -17,6 +17,7 @@ package io.element.android.features.userlist.test import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs @@ -24,5 +25,9 @@ class FakeUserListPresenterFactory( private val fakeUserListPresenter: FakeUserListPresenter = FakeUserListPresenter() ) : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): UserListPresenter = fakeUserListPresenter + override fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ): UserListPresenter = fakeUserListPresenter } From 9105f13fb29a77e75563cda02ede74b95d9d2c68 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 16:53:38 +0200 Subject: [PATCH 16/24] Remove wildcard import --- .../createroom/impl/addpeople/AddPeoplePresenter.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index 21ab9fafbd..6b2774a36e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -18,7 +18,11 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable import io.element.android.features.createroom.impl.CreateRoomDataStore -import io.element.android.features.userlist.api.* +import io.element.android.features.userlist.api.SelectionMode +import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListPresenter +import io.element.android.features.userlist.api.UserListPresenterArgs +import io.element.android.features.userlist.api.UserListState import io.element.android.libraries.architecture.Presenter import javax.inject.Inject import javax.inject.Named From 27a6e5f9e94226e303d0720b224e198105b9d685 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 23:30:33 +0200 Subject: [PATCH 17/24] Use immutableList --- .../features/userlist/api/components/SelectedUsersList.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt index 491fb7827f..6909345a51 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SelectedUsersList.kt @@ -34,10 +34,11 @@ import io.element.android.features.userlist.api.aListOfSelectedUsers import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList @Composable fun SelectedUsersList( - selectedUsers: List, + selectedUsers: ImmutableList, modifier: Modifier = Modifier, autoScroll: Boolean = false, contentPadding: PaddingValues = PaddingValues(0.dp), From ddb49da7f3cf67b63016ffdcd7b66f3ef38b9777 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 23:32:27 +0200 Subject: [PATCH 18/24] rename state provider --- ...opleStateProvider.kt => AddPeopleUserListStateProvider.kt} | 2 +- .../features/createroom/impl/addpeople/AddPeopleView.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/{AddPeopleStateProvider.kt => AddPeopleUserListStateProvider.kt} (95%) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt similarity index 95% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt index 3c9073d0d0..4a4581539b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt @@ -24,7 +24,7 @@ import io.element.android.features.userlist.api.aUserListState import io.element.android.libraries.matrix.ui.components.aMatrixUserList import kotlinx.collections.immutable.toImmutableList -open class AddPeopleStateProvider : PreviewParameterProvider { +open class AddPeopleUserListStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aUserListState(), diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index e81a47a04a..ebf3e9d508 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -108,12 +108,12 @@ fun AddPeopleViewTopBar( @Preview @Composable -internal fun AddPeopleViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: UserListState) = +internal fun AddPeopleViewLightPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun AddPeopleViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: UserListState) = +internal fun AddPeopleViewDarkPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreviewDark { ContentToPreview(state) } @Composable From 841a24a3a4ebbe209f5c89e7025b15ed0fa78b2c Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 13 Apr 2023 23:33:54 +0200 Subject: [PATCH 19/24] Check if user is not already selected --- .../android/features/userlist/api/UserListDataStore.kt | 4 +++- .../features/userlist/impl/DefaultUserListPresenter.kt | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt index c1e982dd59..f9c73950f1 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt @@ -26,7 +26,9 @@ class UserListDataStore @Inject constructor() { private val selectedUsers: MutableStateFlow> = MutableStateFlow(emptyList()) fun selectUser(user: MatrixUser) { - selectedUsers.tryEmit(selectedUsers.value.plus(user)) + if (user !in selectedUsers.value) { + selectedUsers.tryEmit(selectedUsers.value.plus(user)) + } } fun removeUserFromSelection(user: MatrixUser) { diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 965bf40983..06f4caed86 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -72,11 +72,7 @@ class DefaultUserListPresenter @AssistedInject constructor( when (event) { is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active is UserListEvents.UpdateSearchQuery -> searchQuery = event.query - is UserListEvents.AddToSelection -> { - if (event.matrixUser !in selectedUsers.value) { - userListDataStore.selectUser(event.matrixUser) - } - } + is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) } } From 4c2237a63ef182e95906c38158e452e95f24856e Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 14 Apr 2023 00:22:41 +0200 Subject: [PATCH 20/24] Fix unit test --- .../userlist/impl/DefaultUserListPresenterTests.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt index 15c26fb8d3..29432c2ff1 100644 --- a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt +++ b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt @@ -22,6 +22,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.userlist.api.SelectionMode +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListEvents import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.test.FakeUserListDataSource @@ -43,7 +44,8 @@ class DefaultUserListPresenterTests { fun `present - initial state for single selection`() = runTest { val presenter = DefaultUserListPresenter( UserListPresenterArgs(selectionMode = SelectionMode.Single), - userListDataSource + userListDataSource, + UserListDataStore(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -61,7 +63,8 @@ class DefaultUserListPresenterTests { fun `present - initial state for multiple selection`() = runTest { val presenter = DefaultUserListPresenter( UserListPresenterArgs(selectionMode = SelectionMode.Multiple), - userListDataSource + userListDataSource, + UserListDataStore(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -79,7 +82,8 @@ class DefaultUserListPresenterTests { fun `present - update search query`() = runTest { val presenter = DefaultUserListPresenter( UserListPresenterArgs(selectionMode = SelectionMode.Single), - userListDataSource + userListDataSource, + UserListDataStore(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -111,7 +115,8 @@ class DefaultUserListPresenterTests { val presenter = DefaultUserListPresenter( UserListPresenterArgs(selectionMode = SelectionMode.Single), - userListDataSource + userListDataSource, + UserListDataStore(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() From 1bc59bb9abf51aa7f7ec9e9344f41e1e3deaa213 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 14 Apr 2023 09:17:44 +0200 Subject: [PATCH 21/24] update ConfigureRoomPresenter unit tests --- .../ConfigureRoomPresenterTests.kt | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 4828b7e636..47fe698d05 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -23,6 +23,7 @@ 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.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.UserListDataStore import io.element.android.libraries.matrix.test.AN_AVATAR_URL @@ -51,8 +52,11 @@ class ConfigureRoomPresenterTests { presenter.present() }.test { val initialState = awaitItem() + assertThat(initialState.config).isEqualTo(CreateRoomConfig()) assertThat(initialState.config.roomName).isNull() assertThat(initialState.config.topic).isNull() + assertThat(initialState.config.invites).isEmpty() + assertThat(initialState.config.avatarUrl).isNull() assertThat(initialState.config.privacy).isNull() } } @@ -63,24 +67,28 @@ class ConfigureRoomPresenterTests { presenter.present() }.test { val initialState = awaitItem() + var config = initialState.config assertThat(initialState.isCreateButtonEnabled).isFalse() // Room name not empty initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) var newState: ConfigureRoomState = awaitItem() - assertThat(newState.config.roomName).isEqualTo(A_ROOM_NAME) + config = config.copy(roomName = A_ROOM_NAME) + assertThat(newState.config).isEqualTo(config) assertThat(newState.isCreateButtonEnabled).isFalse() // Select privacy - initialState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Private)) + newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Private)) newState = awaitItem() - assertThat(newState.config.privacy).isEqualTo(RoomPrivacy.Private) + config = config.copy(privacy = RoomPrivacy.Private) + assertThat(newState.config).isEqualTo(config) assertThat(newState.isCreateButtonEnabled).isTrue() // Clear room name - initialState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) + newState.eventSink(ConfigureRoomEvents.RoomNameChanged("")) newState = awaitItem() - assertThat(newState.config.roomName).isNull() + config = config.copy(roomName = null) + assertThat(newState.config).isEqualTo(config) assertThat(newState.isCreateButtonEnabled).isFalse() } } @@ -91,26 +99,32 @@ class ConfigureRoomPresenterTests { presenter.present() }.test { val initialState = awaitItem() + var config = initialState.config + // Room name initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) - val stateAfterRoomNameChanged = awaitItem() - assertThat(stateAfterRoomNameChanged.config.roomName).isEqualTo(A_ROOM_NAME) + var newState = awaitItem() + config = config.copy(roomName = A_ROOM_NAME) + assertThat(newState.config).isEqualTo(config) // Room topic - stateAfterRoomNameChanged.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE)) - val stateAfterTopicChanged = awaitItem() - assertThat(stateAfterTopicChanged.config.topic).isEqualTo(A_MESSAGE) + newState.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE)) + newState = awaitItem() + config = config.copy(topic = A_MESSAGE) + assertThat(newState.config).isEqualTo(config) // Room avatar val anUri = Uri.parse(AN_AVATAR_URL) - stateAfterTopicChanged.eventSink(ConfigureRoomEvents.AvatarUriChanged(anUri)) - val stateAfterAvatarUriChanged = awaitItem() - assertThat(stateAfterAvatarUriChanged.config.avatarUrl).isEqualTo(anUri.toString()) + newState.eventSink(ConfigureRoomEvents.AvatarUriChanged(anUri)) + newState = awaitItem() + config = config.copy(avatarUrl = anUri.toString()) + assertThat(newState.config).isEqualTo(config) // Room privacy - stateAfterAvatarUriChanged.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) - val stateAfterPrivacyChanged = awaitItem() - assertThat(stateAfterPrivacyChanged.config.privacy).isEqualTo(RoomPrivacy.Public) + newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) + newState = awaitItem() + config = config.copy(privacy = RoomPrivacy.Public) + assertThat(newState.config).isEqualTo(config) } } } From b59fab03e1fa8829c781b0891f4a20b3ddfe6b8e Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 14 Apr 2023 10:37:20 +0200 Subject: [PATCH 22/24] Fix privacy item binding --- .../features/createroom/impl/configureroom/RoomPrivacy.kt | 2 +- .../features/createroom/impl/configureroom/RoomPrivacyItem.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt index e0b7411680..5cb0cf25b4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacy.kt @@ -17,6 +17,6 @@ package io.element.android.features.createroom.impl.configureroom enum class RoomPrivacy { - Public, Private, + Public, } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt index 0d1f6011c9..462dedba00 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt @@ -38,13 +38,13 @@ fun roomPrivacyItems(): ImmutableList { return RoomPrivacy.values() .map { when (it) { - RoomPrivacy.Public -> RoomPrivacyItem( + RoomPrivacy.Private -> RoomPrivacyItem( privacy = it, icon = Icons.Outlined.Lock, title = stringResource(R.string.screen_create_room_private_option_title), description = stringResource(R.string.screen_create_room_private_option_description), ) - RoomPrivacy.Private -> RoomPrivacyItem( + RoomPrivacy.Public -> RoomPrivacyItem( privacy = it, icon = Icons.Outlined.Public, title = stringResource(R.string.screen_create_room_public_option_title), From 51da3a72254b89ba2b8693268732616b07a636db Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 14 Apr 2023 14:29:12 +0200 Subject: [PATCH 23/24] update screenshots --- ..._ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index c273abf7fb..62e855dab1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32e702b0c3671c6ef9ae38acadccf8c3da48b7093ac81ede95f8ea6f9b28e405 -size 103375 +oid sha256:df7631ad85dc6b3fbdabfc820b11b4e12a6128202e3faa2d105ba22793fe8c22 +size 103428 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 8693cd2e06..4045dae649 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad0d51590e0c66e5711d1a1809a234328717548dc7fac837dc4bce4870caf70a -size 96969 +oid sha256:3c2731ea21ca5b001aadc29ecbb15a2b263b0c042b7344cb10149174ff88ec0a +size 96835 From c9edd8c6cdac237c381d98fb7f8badb70c1d5ab9 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 14 Apr 2023 14:54:38 +0200 Subject: [PATCH 24/24] Add missing test --- .../ConfigureRoomPresenterTests.kt | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 47fe698d05..1f27baa511 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -29,6 +29,9 @@ import io.element.android.features.userlist.api.UserListDataStore import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.ui.components.aMatrixUser +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before @@ -40,10 +43,12 @@ import org.robolectric.RobolectricTestRunner class ConfigureRoomPresenterTests { private lateinit var presenter: ConfigureRoomPresenter + private lateinit var userListDataStore: UserListDataStore @Before fun setup() { - presenter = ConfigureRoomPresenter(CreateRoomDataStore(UserListDataStore())) + userListDataStore = UserListDataStore() + presenter = ConfigureRoomPresenter(CreateRoomDataStore(userListDataStore)) } @Test @@ -99,32 +104,49 @@ class ConfigureRoomPresenterTests { presenter.present() }.test { val initialState = awaitItem() - var config = initialState.config + var expectedConfig = CreateRoomConfig() + assertThat(initialState.config).isEqualTo(expectedConfig) + + // Select User + val selectedUser1 = aMatrixUser() + val selectedUser2 = aMatrixUser("@id_of_bob:server.org", "Bob") + userListDataStore.selectUser(selectedUser1) + skipItems(1) + userListDataStore.selectUser(selectedUser2) + var newState = awaitItem() + expectedConfig = expectedConfig.copy(invites = persistentListOf(selectedUser1, selectedUser2)) + assertThat(newState.config).isEqualTo(expectedConfig) // Room name initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) - var newState = awaitItem() - config = config.copy(roomName = A_ROOM_NAME) - assertThat(newState.config).isEqualTo(config) + newState = awaitItem() + expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME) + assertThat(newState.config).isEqualTo(expectedConfig) // Room topic newState.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE)) newState = awaitItem() - config = config.copy(topic = A_MESSAGE) - assertThat(newState.config).isEqualTo(config) + expectedConfig = expectedConfig.copy(topic = A_MESSAGE) + assertThat(newState.config).isEqualTo(expectedConfig) // Room avatar val anUri = Uri.parse(AN_AVATAR_URL) newState.eventSink(ConfigureRoomEvents.AvatarUriChanged(anUri)) newState = awaitItem() - config = config.copy(avatarUrl = anUri.toString()) - assertThat(newState.config).isEqualTo(config) + expectedConfig = expectedConfig.copy(avatarUrl = anUri.toString()) + assertThat(newState.config).isEqualTo(expectedConfig) // Room privacy newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public)) newState = awaitItem() - config = config.copy(privacy = RoomPrivacy.Public) - assertThat(newState.config).isEqualTo(config) + expectedConfig = expectedConfig.copy(privacy = RoomPrivacy.Public) + assertThat(newState.config).isEqualTo(expectedConfig) + + // Remove user + newState.eventSink(ConfigureRoomEvents.RemoveFromSelection(selectedUser1)) + newState = awaitItem() + expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList()) + assertThat(newState.config).isEqualTo(expectedConfig) } } }