From 86860ac7dfed6dbb99dfd6ed6aae0d50a15feeee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:55:53 +0000 Subject: [PATCH 01/47] Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v1.8.10 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2f7ebd91d2..3bca911956 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10") } } From bb92bb169c81a09a6e6030a5522dfff1502c8213 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:54:01 +0100 Subject: [PATCH 02/47] Adding changelog entry --- changelog.d/108.wip | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/108.wip diff --git a/changelog.d/108.wip b/changelog.d/108.wip new file mode 100644 index 0000000000..e483e7f6ea --- /dev/null +++ b/changelog.d/108.wip @@ -0,0 +1 @@ +[Create and join rooms] Select members before creating a room (UI for selection) From 230c62368c71a8931d9bcdf63e21c1b87753ce5b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:34:15 +0100 Subject: [PATCH 03/47] Adding selectmembers node with navigation --- .../createroom/impl/CreateRoomFlowNode.kt | 15 ++- .../impl/root/CreateRoomRootEvents.kt | 1 - .../impl/root/CreateRoomRootNode.kt | 10 ++ .../impl/root/CreateRoomRootPresenter.kt | 1 - .../impl/root/CreateRoomRootView.kt | 3 +- .../impl/selectmembers/SelectMembersEvents.kt | 19 ++++ .../impl/selectmembers/SelectMembersNode.kt | 46 ++++++++ .../selectmembers/SelectMembersPresenter.kt | 36 +++++++ .../impl/selectmembers/SelectMembersState.kt | 21 ++++ .../SelectMembersStateProvider.kt | 30 ++++++ .../impl/selectmembers/SelectMembersView.kt | 102 ++++++++++++++++++ 11 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt 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 618eda0166..805d11a319 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 @@ -24,10 +24,12 @@ 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.root.CreateRoomRootNode +import io.element.android.features.createroom.impl.selectmembers.SelectMembersNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode @@ -50,11 +52,22 @@ class CreateRoomFlowNode @AssistedInject constructor( sealed interface NavTarget : Parcelable { @Parcelize object Root : NavTarget + + @Parcelize + object NewRoom : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - NavTarget.Root -> createNode(buildContext) + NavTarget.Root -> { + val callback = object : CreateRoomRootNode.Callback { + override fun onCreateNewRoom() { + backstack.push(NavTarget.NewRoom) + } + } + createNode(buildContext, plugins = listOf(callback)) + } + NavTarget.NewRoom -> createNode(buildContext) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt index ddf2003e83..b9e508032a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt @@ -21,7 +21,6 @@ import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface CreateRoomRootEvents { data class UpdateSearchQuery(val query: String) : CreateRoomRootEvents data class StartDM(val matrixUser: MatrixUser) : CreateRoomRootEvents - object CreateRoom : CreateRoomRootEvents object InvitePeople : CreateRoomRootEvents data class OnSearchActiveChanged(val active: Boolean) : CreateRoomRootEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt index 16a965695f..dadc16efc4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier 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.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode @@ -35,6 +36,14 @@ class CreateRoomRootNode @AssistedInject constructor( private val presenter: CreateRoomRootPresenter, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onCreateNewRoom() + } + + private fun onCreateNewRoom() { + plugins().forEach { it.onCreateNewRoom() } + } + sealed interface NavTarget : Parcelable { @Parcelize object Root : NavTarget @@ -47,6 +56,7 @@ class CreateRoomRootNode @AssistedInject constructor( state = state, modifier = modifier, onClosePressed = this::navigateUp, + onNewRoomClicked = this::onCreateNewRoom, ) } } 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 828f531211..23c8d8eaf3 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 @@ -49,7 +49,6 @@ class CreateRoomRootPresenter @Inject constructor() : Presenter isSearchActive = event.active is CreateRoomRootEvents.UpdateSearchQuery -> searchQuery = event.query is CreateRoomRootEvents.StartDM -> handleStartDM(event.matrixUser) - CreateRoomRootEvents.CreateRoom -> Unit // Todo Handle create room action CreateRoomRootEvents.InvitePeople -> Unit // Todo Handle invite people action } } 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 bab42c6964..5417ca82da 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 @@ -65,6 +65,7 @@ fun CreateRoomRootView( state: CreateRoomRootState, modifier: Modifier = Modifier, onClosePressed: () -> Unit = {}, + onNewRoomClicked: () -> Unit = {}, ) { Scaffold( modifier = modifier.fillMaxWidth(), @@ -91,7 +92,7 @@ fun CreateRoomRootView( if (!state.isSearchActive) { CreateRoomActionButtonsList( - onNewRoomClicked = { state.eventSink(CreateRoomRootEvents.CreateRoom) }, + onNewRoomClicked = onNewRoomClicked, onInvitePeopleClicked = { state.eventSink(CreateRoomRootEvents.InvitePeople) }, ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt new file mode 100644 index 0000000000..b751e9249e --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt @@ -0,0 +1,19 @@ +/* + * 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.selectmembers + +sealed interface SelectMembersEvents diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt new file mode 100644 index 0000000000..2fd6227e38 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt @@ -0,0 +1,46 @@ +/* + * 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.selectmembers + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +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.di.AppScope + +@ContributesNode(AppScope::class) +class SelectMembersNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: SelectMembersPresenter, +) : Node(buildContext, plugins = plugins) { + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + SelectMembersView( + state = state, + modifier = modifier, + onBackPressed = { navigateUp() }, + onNextPressed = { }, + ) + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt new file mode 100644 index 0000000000..a434656298 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt @@ -0,0 +1,36 @@ +/* + * 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.selectmembers + +import androidx.compose.runtime.Composable +import io.element.android.libraries.architecture.Presenter +import javax.inject.Inject + +class SelectMembersPresenter @Inject constructor() : Presenter { + + @Composable + override fun present(): SelectMembersState { + + fun handleEvents(event: SelectMembersEvents) { + // do nothing for now + } + + return SelectMembersState( + eventSink = ::handleEvents + ) + } +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt new file mode 100644 index 0000000000..7e78e48126 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.createroom.impl.selectmembers + +data class SelectMembersState( + val eventSink: (SelectMembersEvents) -> Unit, +) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt new file mode 100644 index 0000000000..0ed1ae14f7 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.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.selectmembers + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +open class SelectMembersStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aSelectMembersState(), + ) +} + +fun aSelectMembersState() = SelectMembersState( + eventSink = {} +) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt new file mode 100644 index 0000000000..100a4f495d --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 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.selectmembers + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.sp +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.CenterAlignedTopAppBar +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.R + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectMembersView( + state: SelectMembersState, + modifier: Modifier = Modifier, + onBackPressed: () -> Unit = {}, + onNextPressed: () -> Unit = {}, +) { + val eventSink = state.eventSink + + Scaffold( + topBar = { + SelectMembersViewTopBar( + onBackPressed = onBackPressed, + onNextPressed = onNextPressed, + ) + } + ) { padding -> + Box( + modifier = modifier + .fillMaxSize() + .padding(padding) + ) { + Text(text = "Hello!") + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectMembersViewTopBar( + modifier: Modifier = Modifier, + onBackPressed: () -> Unit = {}, + onNextPressed: () -> Unit = {}, +) { + CenterAlignedTopAppBar( + modifier = modifier, + title = { + Text( + text = stringResource(id = R.string.add_people), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + ) + }, + navigationIcon = { BackButton(onClick = onBackPressed) }, + actions = { + + } + ) +} + +@Preview +@Composable +internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectMembersStateProvider::class) state: SelectMembersState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun ChangeServerViewDarkPreview(@PreviewParameter(SelectMembersStateProvider::class) state: SelectMembersState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: SelectMembersState) { + SelectMembersView(state = state) +} From 787a5bc1cb677e5bf5b7fa65d7a0c0b0d16fe277 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Mon, 13 Mar 2023 14:54:39 +0100 Subject: [PATCH 04/47] Adding action in the top bar --- .../impl/selectmembers/SelectMembersState.kt | 1 + .../SelectMembersStateProvider.kt | 1 + .../impl/selectmembers/SelectMembersView.kt | 21 +++++++++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt index 7e78e48126..58bbd033a3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt @@ -17,5 +17,6 @@ package io.element.android.features.createroom.impl.selectmembers data class SelectMembersState( + val selectedUserIds: List = emptyList(), val eventSink: (SelectMembersEvents) -> Unit, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt index 0ed1ae14f7..6acff4d979 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt @@ -22,6 +22,7 @@ open class SelectMembersStateProvider : PreviewParameterProvider get() = sequenceOf( aSelectMembersState(), + aSelectMembersState().copy(selectedUserIds = listOf("someUserId")) ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt index 100a4f495d..2443405251 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -33,7 +34,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.ui.strings.R +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,6 +50,7 @@ fun SelectMembersView( Scaffold( topBar = { SelectMembersViewTopBar( + hasSelectedUsers = state.selectedUserIds.isNotEmpty(), onBackPressed = onBackPressed, onNextPressed = onNextPressed, ) @@ -58,7 +61,7 @@ fun SelectMembersView( .fillMaxSize() .padding(padding) ) { - Text(text = "Hello!") + } } } @@ -66,6 +69,7 @@ fun SelectMembersView( @OptIn(ExperimentalMaterial3Api::class) @Composable fun SelectMembersViewTopBar( + hasSelectedUsers: Boolean, modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, onNextPressed: () -> Unit = {}, @@ -74,14 +78,23 @@ fun SelectMembersViewTopBar( modifier = modifier, title = { Text( - text = stringResource(id = R.string.add_people), + text = stringResource(id = StringR.string.add_people), fontSize = 16.sp, fontWeight = FontWeight.SemiBold, ) }, navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { - + TextButton( + modifier = Modifier.padding(horizontal = 8.dp), + onClick = onNextPressed, + ) { + val textActionResId = if (hasSelectedUsers) StringR.string.action_next else StringR.string.action_skip + Text( + text = stringResource(id = textActionResId), + fontSize = 16.sp, + ) + } } ) } From ab538c46be5d7eb15b57fa4565fe2a6dc1c63d2d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Mon, 13 Mar 2023 16:17:55 +0100 Subject: [PATCH 05/47] Adding a view to list the selected users --- .../impl/selectmembers/SelectMembersState.kt | 4 +- .../SelectMembersStateProvider.kt | 15 +++- .../impl/selectmembers/SelectMembersView.kt | 83 ++++++++++++++++++- 3 files changed, 97 insertions(+), 5 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt index 58bbd033a3..bbcb205f9b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt @@ -16,7 +16,9 @@ package io.element.android.features.createroom.impl.selectmembers +import io.element.android.libraries.matrix.ui.model.MatrixUser + data class SelectMembersState( - val selectedUserIds: List = emptyList(), + val selectedUsers: List = emptyList(), val eventSink: (SelectMembersEvents) -> Unit, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt index 6acff4d979..36da523160 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt @@ -17,15 +17,28 @@ package io.element.android.features.createroom.impl.selectmembers import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.ui.model.MatrixUser open class SelectMembersStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSelectMembersState(), - aSelectMembersState().copy(selectedUserIds = listOf("someUserId")) + aSelectMembersState().copy( + selectedUsers = listOf( + aMatrixUser(userName = "User"), + aMatrixUser(userName = "User with long name"), + ) + ) ) } fun aSelectMembersState() = SelectMembersState( eventSink = {} ) + +fun aMatrixUser(userName: String): MatrixUser { + return MatrixUser(id = UserId("@id"), username = userName, avatarData = AvatarData("@id", "U", size = AvatarSize.BIG)) +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt index 2443405251..a0a313b727 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt @@ -16,25 +16,43 @@ package io.element.android.features.createroom.impl.selectmembers +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +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.material3.ExperimentalMaterial3Api +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.font.FontWeight +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 androidx.compose.ui.unit.sp +import io.element.android.libraries.designsystem.components.avatar.Avatar 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.CenterAlignedTopAppBar +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.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) @@ -50,18 +68,23 @@ fun SelectMembersView( Scaffold( topBar = { SelectMembersViewTopBar( - hasSelectedUsers = state.selectedUserIds.isNotEmpty(), + hasSelectedUsers = state.selectedUsers.isNotEmpty(), onBackPressed = onBackPressed, onNextPressed = onNextPressed, ) } ) { padding -> - Box( + Column( modifier = modifier .fillMaxSize() .padding(padding) ) { - + SelectedMembersList( + modifier = Modifier.padding(horizontal = 16.dp), + selectedUsers = state.selectedUsers, + ) + // TODO create ViewEvent to add/remove selected user + // TODO create a SearchUserView with multi selection option + callbacks } } } @@ -99,6 +122,60 @@ fun SelectMembersViewTopBar( ) } +@Composable +fun SelectedMembersList( + selectedUsers: List, + modifier: Modifier = Modifier, + onUserRemoved: (MatrixUser) -> Unit = {}, +) { + LazyRow( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + items(selectedUsers) { matrixUser -> + SelectedMember( + matrixUser = matrixUser, + onUserRemoved = onUserRemoved, + ) + } + } +} + +@Composable +fun SelectedMember( + matrixUser: MatrixUser, + modifier: Modifier = Modifier, + onUserRemoved: (MatrixUser) -> Unit, +) { + Box(modifier = modifier.width(56.dp)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Avatar(matrixUser.avatarData) + Text( + text = matrixUser.username.orEmpty(), + 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 ChangeServerViewLightPreview(@PreviewParameter(SelectMembersStateProvider::class) state: SelectMembersState) = From 8c7eb3fabd55cea7d604adf19888ce7f4682bf30 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Mon, 13 Mar 2023 17:07:36 +0100 Subject: [PATCH 06/47] Logic to store the currently selected users --- .../impl/selectmembers/SelectMembersEvents.kt | 7 ++++++- .../selectmembers/SelectMembersPresenter.kt | 17 +++++++++++++++-- .../impl/selectmembers/SelectMembersView.kt | 4 ++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt index b751e9249e..7343cb3377 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt @@ -16,4 +16,9 @@ package io.element.android.features.createroom.impl.selectmembers -sealed interface SelectMembersEvents +import io.element.android.libraries.matrix.ui.model.MatrixUser + +sealed interface SelectMembersEvents { + data class AddToSelection(val matrixUser: MatrixUser) : SelectMembersEvents + data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectMembersEvents +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt index a434656298..ac8616985d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt @@ -17,20 +17,33 @@ package io.element.android.features.createroom.impl.selectmembers import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.ui.model.MatrixUser import javax.inject.Inject +// TODO add unit tests class SelectMembersPresenter @Inject constructor() : Presenter { @Composable override fun present(): SelectMembersState { + val selectedUsers = rememberSaveable { mutableStateOf(emptyList()) } fun handleEvents(event: SelectMembersEvents) { - // do nothing for now + when (event) { + is SelectMembersEvents.AddToSelection -> selectedUsers.value = mutableListOf().also { + it.addAll(selectedUsers.value.plus(event.matrixUser)) + } + is SelectMembersEvents.RemoveFromSelection -> selectedUsers.value = mutableListOf().also { + it.addAll(selectedUsers.value.minus(event.matrixUser)) + } + } } return SelectMembersState( - eventSink = ::handleEvents + selectedUsers = selectedUsers.value, + eventSink = ::handleEvents, ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt index a0a313b727..9ff946f98f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersView.kt @@ -79,12 +79,12 @@ fun SelectMembersView( .fillMaxSize() .padding(padding) ) { + // TODO create a SearchUserView with multi selection option + callbacks SelectedMembersList( modifier = Modifier.padding(horizontal = 16.dp), selectedUsers = state.selectedUsers, + onUserRemoved = { eventSink(SelectMembersEvents.RemoveFromSelection(it)) } ) - // TODO create ViewEvent to add/remove selected user - // TODO create a SearchUserView with multi selection option + callbacks } } } From c5d7a6c89dd616837b5d5ff782354a53b72b1199 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:31:22 +0100 Subject: [PATCH 07/47] Removing default value in ViewState + use ImmutableList type --- .../impl/selectmembers/SelectMembersPresenter.kt | 16 ++++++++-------- .../impl/selectmembers/SelectMembersState.kt | 3 ++- .../selectmembers/SelectMembersStateProvider.kt | 7 ++++--- .../impl/selectmembers/SelectMembersView.kt | 1 + 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt index ac8616985d..9ea20b3df9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt @@ -17,10 +17,14 @@ package io.element.android.features.createroom.impl.selectmembers import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.remember import io.element.android.libraries.architecture.Presenter 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 javax.inject.Inject // TODO add unit tests @@ -28,16 +32,12 @@ class SelectMembersPresenter @Inject constructor() : Presenter()) } + val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } fun handleEvents(event: SelectMembersEvents) { when (event) { - is SelectMembersEvents.AddToSelection -> selectedUsers.value = mutableListOf().also { - it.addAll(selectedUsers.value.plus(event.matrixUser)) - } - is SelectMembersEvents.RemoveFromSelection -> selectedUsers.value = mutableListOf().also { - it.addAll(selectedUsers.value.minus(event.matrixUser)) - } + is SelectMembersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + is SelectMembersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt index bbcb205f9b..500a6e1570 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt @@ -17,8 +17,9 @@ package io.element.android.features.createroom.impl.selectmembers import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList data class SelectMembersState( - val selectedUsers: List = emptyList(), + val selectedUsers: ImmutableList, val eventSink: (SelectMembersEvents) -> Unit, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt index 36da523160..8b2bfa9b30 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt @@ -18,16 +18,16 @@ package io.element.android.features.createroom.impl.selectmembers import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.persistentListOf open class SelectMembersStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSelectMembersState(), aSelectMembersState().copy( - selectedUsers = listOf( + selectedUsers = persistentListOf( aMatrixUser(userName = "User"), aMatrixUser(userName = "User with long name"), ) @@ -36,9 +36,10 @@ open class SelectMembersStateProvider : PreviewParameterProvider Date: Tue, 14 Mar 2023 14:47:27 +0100 Subject: [PATCH 08/47] Renaming package to selectusers --- .../createroom/impl/CreateRoomFlowNode.kt | 4 +-- .../SelectUsersEvents.kt} | 8 ++--- .../SelectUsersNode.kt} | 8 ++--- .../SelectUsersPresenter.kt} | 14 ++++----- .../SelectUsersState.kt} | 6 ++-- .../SelectUsersStateProvider.kt} | 12 ++++---- .../SelectUsersView.kt} | 29 +++++++++---------- 7 files changed, 40 insertions(+), 41 deletions(-) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersEvents.kt => selectusers/SelectUsersEvents.kt} (84%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersNode.kt => selectusers/SelectUsersNode.kt} (87%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersPresenter.kt => selectusers/SelectUsersPresenter.kt} (70%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersState.kt => selectusers/SelectUsersState.kt} (83%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersStateProvider.kt => selectusers/SelectUsersStateProvider.kt} (80%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectmembers/SelectMembersView.kt => selectusers/SelectUsersView.kt} (90%) 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 805d11a319..f03decf561 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 @@ -29,7 +29,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.impl.root.CreateRoomRootNode -import io.element.android.features.createroom.impl.selectmembers.SelectMembersNode +import io.element.android.features.createroom.impl.selectusers.SelectUsersNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode @@ -67,7 +67,7 @@ class CreateRoomFlowNode @AssistedInject constructor( } createNode(buildContext, plugins = listOf(callback)) } - NavTarget.NewRoom -> createNode(buildContext) + NavTarget.NewRoom -> createNode(buildContext) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt similarity index 84% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt index 7343cb3377..97e6af4d7e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectmembers +package io.element.android.features.createroom.impl.selectusers import io.element.android.libraries.matrix.ui.model.MatrixUser -sealed interface SelectMembersEvents { - data class AddToSelection(val matrixUser: MatrixUser) : SelectMembersEvents - data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectMembersEvents +sealed interface SelectUsersEvents { + data class AddToSelection(val matrixUser: MatrixUser) : SelectUsersEvents + data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectUsersEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt similarity index 87% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt index 2fd6227e38..7ca9747c5c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectmembers +package io.element.android.features.createroom.impl.selectusers import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -27,16 +27,16 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.AppScope @ContributesNode(AppScope::class) -class SelectMembersNode @AssistedInject constructor( +class SelectUsersNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenter: SelectMembersPresenter, + private val presenter: SelectUsersPresenter, ) : Node(buildContext, plugins = plugins) { @Composable override fun View(modifier: Modifier) { val state = presenter.present() - SelectMembersView( + SelectUsersView( state = state, modifier = modifier, onBackPressed = { navigateUp() }, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt similarity index 70% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt index 9ea20b3df9..bd040f91a0 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectmembers +package io.element.android.features.createroom.impl.selectusers import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -28,20 +28,20 @@ import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject // TODO add unit tests -class SelectMembersPresenter @Inject constructor() : Presenter { +class SelectUsersPresenter @Inject constructor() : Presenter { @Composable - override fun present(): SelectMembersState { + override fun present(): SelectUsersState { val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } - fun handleEvents(event: SelectMembersEvents) { + fun handleEvents(event: SelectUsersEvents) { when (event) { - is SelectMembersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() - is SelectMembersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() } } - return SelectMembersState( + return SelectUsersState( selectedUsers = selectedUsers.value, eventSink = ::handleEvents, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt similarity index 83% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt index 500a6e1570..84c0da9466 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectmembers +package io.element.android.features.createroom.impl.selectusers import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList -data class SelectMembersState( +data class SelectUsersState( val selectedUsers: ImmutableList, - val eventSink: (SelectMembersEvents) -> Unit, + val eventSink: (SelectUsersEvents) -> Unit, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt similarity index 80% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt index 8b2bfa9b30..4dd64013c3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectmembers/SelectMembersStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectmembers +package io.element.android.features.createroom.impl.selectusers import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -22,11 +22,11 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.persistentListOf -open class SelectMembersStateProvider : PreviewParameterProvider { - override val values: Sequence +open class SelectUsersStateProvider : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( - aSelectMembersState(), - aSelectMembersState().copy( + aSelectUsersState(), + aSelectUsersState().copy( selectedUsers = persistentListOf( aMatrixUser(userName = "User"), aMatrixUser(userName = "User with long name"), @@ -35,7 +35,7 @@ open class SelectMembersStateProvider : PreviewParameterProvider Unit = {}, onNextPressed: () -> Unit = {}, @@ -67,7 +67,7 @@ fun SelectMembersView( Scaffold( topBar = { - SelectMembersViewTopBar( + SelectUsersViewTopBar( hasSelectedUsers = state.selectedUsers.isNotEmpty(), onBackPressed = onBackPressed, onNextPressed = onNextPressed, @@ -80,10 +80,10 @@ fun SelectMembersView( .padding(padding) ) { // TODO create a SearchUserView with multi selection option + callbacks - SelectedMembersList( + SelectedUsersList( modifier = Modifier.padding(horizontal = 16.dp), selectedUsers = state.selectedUsers, - onUserRemoved = { eventSink(SelectMembersEvents.RemoveFromSelection(it)) } + onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) } ) } } @@ -91,7 +91,7 @@ fun SelectMembersView( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SelectMembersViewTopBar( +fun SelectUsersViewTopBar( hasSelectedUsers: Boolean, modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, @@ -123,7 +123,7 @@ fun SelectMembersViewTopBar( } @Composable -fun SelectedMembersList( +fun SelectedUsersList( selectedUsers: List, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit = {}, @@ -133,7 +133,7 @@ fun SelectedMembersList( horizontalArrangement = Arrangement.spacedBy(24.dp), ) { items(selectedUsers) { matrixUser -> - SelectedMember( + SelectedUser( matrixUser = matrixUser, onUserRemoved = onUserRemoved, ) @@ -142,7 +142,7 @@ fun SelectedMembersList( } @Composable -fun SelectedMember( +fun SelectedUser( matrixUser: MatrixUser, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit, @@ -151,7 +151,6 @@ fun SelectedMember( Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - // TODO set the size using custom Avatar size set to 56.dp Avatar(matrixUser.avatarData) Text( text = matrixUser.username.orEmpty(), @@ -179,15 +178,15 @@ fun SelectedMember( @Preview @Composable -internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectMembersStateProvider::class) state: SelectMembersState) = +internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun ChangeServerViewDarkPreview(@PreviewParameter(SelectMembersStateProvider::class) state: SelectMembersState) = +internal fun ChangeServerViewDarkPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = ElementPreviewDark { ContentToPreview(state) } @Composable -private fun ContentToPreview(state: SelectMembersState) { - SelectMembersView(state = state) +private fun ContentToPreview(state: SelectUsersState) { + SelectUsersView(state = state) } From 8fb4a5d106fac6136d17654f724967f56c305255 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:58:36 +0100 Subject: [PATCH 09/47] Using custom size for avatar of selected user item --- .../features/createroom/impl/selectusers/SelectUsersView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt index b18e237178..421879b522 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp 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 @@ -151,7 +152,7 @@ fun SelectedUser( Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - Avatar(matrixUser.avatarData) + Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56))) Text( text = matrixUser.username.orEmpty(), overflow = TextOverflow.Ellipsis, From de05c761ce1444e65e78bd7ac3d97a74b1ab70ed Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:27:53 +0100 Subject: [PATCH 10/47] Use getBestName() for a matrix user name --- .../createroom/impl/selectusers/SelectUsersStateProvider.kt | 1 + .../features/createroom/impl/selectusers/SelectUsersView.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt index 4dd64013c3..c7b5956c99 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt @@ -28,6 +28,7 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState(), aSelectUsersState().copy( selectedUsers = persistentListOf( + aMatrixUser(userName = ""), aMatrixUser(userName = "User"), aMatrixUser(userName = "User with long name"), ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt index 421879b522..020b9c8d7b 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt @@ -54,6 +54,7 @@ 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.matrix.ui.model.getBestName import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class) @@ -154,7 +155,7 @@ fun SelectedUser( ) { Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56))) Text( - text = matrixUser.username.orEmpty(), + text = matrixUser.getBestName(), overflow = TextOverflow.Ellipsis, maxLines = 1, style = MaterialTheme.typography.bodyLarge, From 31f8c92b58e8ea0eddd5abd944e58b26a861a1a9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Tue, 14 Mar 2023 17:27:41 +0100 Subject: [PATCH 11/47] Create a reusable selectusers feature module --- .../createroom/impl/CreateRoomFlowNode.kt | 4 +- .../impl/addpeople/AddPeopleEvents.kt | 24 +++ .../AddPeopleNode.kt} | 8 +- .../AddPeoplePresenter.kt} | 16 +- .../impl/addpeople/AddPeopleState.kt | 25 +++ .../impl/addpeople/AddPeopleStateProvider.kt | 46 +++++ .../impl/addpeople/AddPeopleView.kt | 120 +++++++++++ features/selectusers/api/build.gradle.kts | 30 +++ .../selectusers/api}/SelectUsersEvents.kt | 3 +- .../selectusers/api/SelectUsersPresenter.kt | 21 ++ .../selectusers/api}/SelectUsersState.kt | 4 +- .../api}/SelectUsersStateProvider.kt | 4 +- .../selectusers/api}/SelectUsersView.kt | 190 ++++++++++++------ features/selectusers/impl/build.gradle.kts | 55 +++++ .../impl/src/main/AndroidManifest.xml | 20 ++ .../impl/DefaultSelectUsersPresenter.kt | 89 ++++++++ settings.gradle.kts | 2 + 17 files changed, 579 insertions(+), 82 deletions(-) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectusers/SelectUsersNode.kt => addpeople/AddPeopleNode.kt} (88%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/{selectusers/SelectUsersPresenter.kt => addpeople/AddPeoplePresenter.kt} (66%) create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt create mode 100644 features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt create mode 100644 features/selectusers/api/build.gradle.kts rename features/{createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers => selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api}/SelectUsersEvents.kt (87%) create mode 100644 features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt rename features/{createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers => selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api}/SelectUsersState.kt (86%) rename features/{createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers => selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api}/SelectUsersStateProvider.kt (93%) rename features/{createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers => selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api}/SelectUsersView.kt (53%) create mode 100644 features/selectusers/impl/build.gradle.kts create mode 100644 features/selectusers/impl/src/main/AndroidManifest.xml create mode 100644 features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt 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 f03decf561..13c3ff52be 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 @@ -28,8 +28,8 @@ 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.root.CreateRoomRootNode -import io.element.android.features.createroom.impl.selectusers.SelectUsersNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode @@ -67,7 +67,7 @@ class CreateRoomFlowNode @AssistedInject constructor( } createNode(buildContext, plugins = listOf(callback)) } - NavTarget.NewRoom -> createNode(buildContext) + NavTarget.NewRoom -> createNode(buildContext) } } 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 new file mode 100644 index 0000000000..c7ce6308e1 --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleEvents.kt @@ -0,0 +1,24 @@ +/* + * 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.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList + +sealed interface AddPeopleEvents { + data class UpdateSelection(val users: ImmutableList) : AddPeopleEvents +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt similarity index 88% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index 7ca9747c5c..751ab7c53f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -27,16 +27,16 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.AppScope @ContributesNode(AppScope::class) -class SelectUsersNode @AssistedInject constructor( +class AddPeopleNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenter: SelectUsersPresenter, + private val presenter: AddPeoplePresenter, ) : Node(buildContext, plugins = plugins) { @Composable override fun View(modifier: Modifier) { val state = presenter.present() - SelectUsersView( + AddPeopleView( state = state, modifier = modifier, onBackPressed = { navigateUp() }, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt similarity index 66% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt rename to features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index bd040f91a0..4cca64b4dd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -24,24 +24,22 @@ import io.element.android.libraries.architecture.Presenter 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 javax.inject.Inject // TODO add unit tests -class SelectUsersPresenter @Inject constructor() : Presenter { +class AddPeoplePresenter @Inject constructor() : Presenter { @Composable - override fun present(): SelectUsersState { - val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } + override fun present(): AddPeopleState { + val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf(aMatrixUser("test"))) } - fun handleEvents(event: SelectUsersEvents) { + fun handleEvents(event: AddPeopleEvents) { when (event) { - is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() - is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + is AddPeopleEvents.UpdateSelection -> selectedUsers.value = event.users } } - return SelectUsersState( + return AddPeopleState( selectedUsers = selectedUsers.value, eventSink = ::handleEvents, ) 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 new file mode 100644 index 0000000000..6214e2259a --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleState.kt @@ -0,0 +1,25 @@ +/* + * 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.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.ImmutableList + +data class AddPeopleState( + val selectedUsers: ImmutableList, + 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 new file mode 100644 index 0000000000..7d7c1851ff --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleStateProvider.kt @@ -0,0 +1,46 @@ +/* + * 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 androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.collections.immutable.persistentListOf + +open class AddPeopleStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aAddPeopleState(), + aAddPeopleState().copy( + selectedUsers = persistentListOf( + aMatrixUser(userName = ""), + aMatrixUser(userName = "User"), + aMatrixUser(userName = "User with long name"), + ) + ) + ) +} + +fun aAddPeopleState() = AddPeopleState( + selectedUsers = persistentListOf(), + eventSink = {} +) + +fun aMatrixUser(userName: String): MatrixUser { + return MatrixUser(id = UserId("@id"), username = userName, avatarData = AvatarData("@id", "U")) +} 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 new file mode 100644 index 0000000000..62e935079a --- /dev/null +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022 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 androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +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.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.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.CenterAlignedTopAppBar +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.ui.strings.R as StringR + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddPeopleView( + state: AddPeopleState, + modifier: Modifier = Modifier, + onBackPressed: () -> Unit = {}, + onNextPressed: () -> Unit = {}, +) { + var isSearchActive by rememberSaveable { mutableStateOf(false) } + val eventSink = state.eventSink + + Scaffold( + topBar = { + AddPeopleViewTopBar( + hasSelectedUsers = state.selectedUsers.isNotEmpty(), + onBackPressed = onBackPressed, + onNextPressed = onNextPressed, + ) + } + ) { padding -> + Column( + modifier = modifier + .fillMaxSize() + .padding(padding), + ) { + // TODO use reusable searchUser bar with multi selection + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddPeopleViewTopBar( + hasSelectedUsers: Boolean, + modifier: Modifier = Modifier, + onBackPressed: () -> Unit = {}, + onNextPressed: () -> Unit = {}, +) { + CenterAlignedTopAppBar( + modifier = modifier, + title = { + Text( + text = stringResource(id = StringR.string.add_people), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + ) + }, + navigationIcon = { BackButton(onClick = onBackPressed) }, + actions = { + TextButton( + modifier = Modifier.padding(horizontal = 8.dp), + onClick = onNextPressed, + ) { + val textActionResId = if (hasSelectedUsers) StringR.string.action_next else StringR.string.action_skip + Text( + text = stringResource(id = textActionResId), + fontSize = 16.sp, + ) + } + } + ) +} + +@Preview +@Composable +internal fun ChangeServerViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun ChangeServerViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: AddPeopleState) { + AddPeopleView(state = state) +} diff --git a/features/selectusers/api/build.gradle.kts b/features/selectusers/api/build.gradle.kts new file mode 100644 index 0000000000..8454780482 --- /dev/null +++ b/features/selectusers/api/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-compose-library") +} + +android { + namespace = "io.element.android.features.selectusers.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.uiStrings) + implementation(projects.libraries.matrixui) +} diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt similarity index 87% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt index 97e6af4d7e..2d13a3475e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersEvents.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.selectusers.api import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface SelectUsersEvents { + data class UpdateSearchQuery(val query: String) : SelectUsersEvents data class AddToSelection(val matrixUser: MatrixUser) : SelectUsersEvents data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectUsersEvents } diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt new file mode 100644 index 0000000000..de889c80b3 --- /dev/null +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.api + +import io.element.android.libraries.architecture.Presenter + +interface SelectUsersPresenter : Presenter diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt similarity index 86% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 84c0da9466..5d89010399 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -14,12 +14,14 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.selectusers.api import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList data class SelectUsersState( + val searchQuery: String, + val searchResults: ImmutableList, val selectedUsers: ImmutableList, val eventSink: (SelectUsersEvents) -> Unit, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt similarity index 93% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index c7b5956c99..6f0c71a0d3 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.selectusers.api import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -37,6 +37,8 @@ open class SelectUsersStateProvider : PreviewParameterProvider } fun aSelectUsersState() = SelectUsersState( + searchQuery = "", + searchResults = persistentListOf(), selectedUsers = persistentListOf(), eventSink = {} ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt similarity index 53% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 020b9c8d7b..f49b7984f8 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/selectusers/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. @@ -14,47 +14,55 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.selectusers +package io.element.android.features.selectusers.api import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width 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.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue 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.font.FontWeight 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 androidx.compose.ui.unit.sp 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.CenterAlignedTopAppBar +import io.element.android.libraries.designsystem.theme.components.DockedSearchBar 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.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.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 @OptIn(ExperimentalMaterial3Api::class) @@ -62,66 +70,38 @@ import io.element.android.libraries.ui.strings.R as StringR fun SelectUsersView( state: SelectUsersState, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onNextPressed: () -> Unit = {}, + onSearchActiveChanged: (Boolean) -> Unit = {}, + onSelectionChanged: (ImmutableList) -> Unit = {}, ) { + var isSearchActive by rememberSaveable { mutableStateOf(false) } val eventSink = state.eventSink - Scaffold( - topBar = { - SelectUsersViewTopBar( - hasSelectedUsers = state.selectedUsers.isNotEmpty(), - onBackPressed = onBackPressed, - onNextPressed = onNextPressed, - ) - } - ) { padding -> - Column( - modifier = modifier - .fillMaxSize() - .padding(padding) - ) { - // TODO create a SearchUserView with multi selection option + callbacks - SelectedUsersList( - modifier = Modifier.padding(horizontal = 16.dp), - selectedUsers = state.selectedUsers, - onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) } - ) - } - } -} + // TODO how to pass back the selection list? -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SelectUsersViewTopBar( - hasSelectedUsers: Boolean, - modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, - onNextPressed: () -> Unit = {}, -) { - CenterAlignedTopAppBar( - modifier = modifier, - title = { - Text( - text = stringResource(id = StringR.string.add_people), - fontSize = 16.sp, - fontWeight = FontWeight.SemiBold, - ) - }, - navigationIcon = { BackButton(onClick = onBackPressed) }, - actions = { - TextButton( - modifier = Modifier.padding(horizontal = 8.dp), - onClick = onNextPressed, - ) { - val textActionResId = if (hasSelectedUsers) StringR.string.action_next else StringR.string.action_skip - Text( - text = stringResource(id = textActionResId), - fontSize = 16.sp, - ) - } - } - ) + Column( + modifier = modifier + .fillMaxSize() + ) { + SearchUserBar( + modifier = Modifier.fillMaxWidth(), + query = state.searchQuery, + results = state.searchResults, + active = isSearchActive, + onActiveChanged = { + isSearchActive = it + onSearchActiveChanged(it) + }, + onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, + onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) } + ) + + // TODO move into search content + SelectedUsersList( + modifier = Modifier.padding(16.dp), + selectedUsers = state.selectedUsers, + onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) } + ) + } } @Composable @@ -178,6 +158,88 @@ fun SelectedUser( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SearchUserBar( + query: String, + results: ImmutableList, + active: Boolean, + modifier: Modifier = Modifier, + placeHolderTitle: String = stringResource(StringR.string.search_for_someone), + onActiveChanged: (Boolean) -> Unit = {}, + onTextChanged: (String) -> Unit = {}, + onResultSelected: (MatrixUser) -> Unit = {}, +) { + val focusManager = LocalFocusManager.current + + if (!active) { + onTextChanged("") + focusManager.clearFocus() + } + + DockedSearchBar( + 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.a11y_clear)) + } + } + } + !active -> { + { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(StringR.string.search), + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + } + } + else -> null + }, + shape = if (!active) SearchBarDefaults.dockedShape else SearchBarDefaults.fullScreenShape, + colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), + content = { + results.forEach { + SearchUserResultItem( + matrixUser = it, + onClick = { onResultSelected(it) } + ) + } + }, + ) +} + +@Composable +fun SearchUserResultItem( + matrixUser: MatrixUser, + modifier: Modifier = Modifier, + onClick: () -> Unit = {}, +) { + MatrixUserRow( + modifier = modifier.heightIn(min = 56.dp), + matrixUser = matrixUser, + avatarSize = AvatarSize.Custom(36), + onClick = onClick, + ) +} + @Preview @Composable internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = diff --git a/features/selectusers/impl/build.gradle.kts b/features/selectusers/impl/build.gradle.kts new file mode 100644 index 0000000000..e74996cc5c --- /dev/null +++ b/features/selectusers/impl/build.gradle.kts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 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. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + alias(libs.plugins.ksp) +} + +android { + namespace = "io.element.android.features.selectusers.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(projects.anvilannotations) + anvil(projects.anvilcodegen) + implementation(projects.libraries.core) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.elementresources) + implementation(projects.libraries.testtags) + implementation(projects.libraries.uiStrings) + api(projects.features.selectusers.api) + ksp(libs.showkase.processor) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + + androidTestImplementation(libs.test.junitext) +} diff --git a/features/selectusers/impl/src/main/AndroidManifest.xml b/features/selectusers/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e9c0841b6b --- /dev/null +++ b/features/selectusers/impl/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt new file mode 100644 index 0000000000..12bc44c7e9 --- /dev/null +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.selectusers.api.SelectUsersEvents +import io.element.android.features.selectusers.api.SelectUsersPresenter +import io.element.android.features.selectusers.api.SelectUsersState +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 +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 javax.inject.Inject + +// TODO add unit tests +@ContributesBinding(SessionScope::class) +class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { + + @Composable + override fun present(): SelectUsersState { + val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } + var searchQuery by rememberSaveable { mutableStateOf("") } + val searchResults: MutableState> = remember { + mutableStateOf(persistentListOf()) + } + + fun handleEvents(event: SelectUsersEvents) { + when (event) { + is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query + is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + } + } + + LaunchedEffect(searchQuery) { + searchResults.value = if (MatrixPatterns.isUserId(searchQuery)) { + persistentListOf(MatrixUser(UserId(searchQuery))) + } else { + persistentListOf() + } + if (searchQuery.isNotEmpty()) { + searchResults.value = performSearch(searchQuery) + } + } + + return SelectUsersState( + searchQuery = searchQuery, + searchResults = searchResults.value, + selectedUsers = selectedUsers.value, + eventSink = ::handleEvents, + ) + } + + private fun performSearch(query: String): ImmutableList { + val isMatrixId = MatrixPatterns.isUserId(query) + val results = mutableListOf()// TODO trigger /search request + if (isMatrixId && results.none { it.id.value == query }) { + val getProfileResult: MatrixUser? = null // TODO trigger /profile request + val profile = getProfileResult ?: MatrixUser(UserId(query)) + results.add(0, profile) + } + return results.toImmutableList() + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 0fd8bc2938..04636090ec 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -92,3 +92,5 @@ include(":features:createroom:api") include(":features:createroom:impl") include(":features:verifysession:api") include(":features:verifysession:impl") +include(":features:selectusers:api") +include(":features:selectusers:impl") From bca9827a2f62db13e11b623b394a926dd53168c3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Tue, 14 Mar 2023 17:53:55 +0100 Subject: [PATCH 12/47] Fixes after rebase --- features/selectusers/api/build.gradle.kts | 1 + .../selectusers/api/SelectUsersEvents.kt | 1 + .../selectusers/api/SelectUsersState.kt | 1 + .../api/SelectUsersStateProvider.kt | 33 +++++++--- .../selectusers/api/SelectUsersView.kt | 46 ++++++-------- .../impl/DefaultSelectUsersPresenter.kt | 2 + .../impl/DefaultSelectUsersPresenterTests.kt | 62 +++++++++++++++++++ 7 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt diff --git a/features/selectusers/api/build.gradle.kts b/features/selectusers/api/build.gradle.kts index 8454780482..d46ed2fbf1 100644 --- a/features/selectusers/api/build.gradle.kts +++ b/features/selectusers/api/build.gradle.kts @@ -26,5 +26,6 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) } diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt index 2d13a3475e..e0ee6ddf68 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersEvents.kt @@ -22,4 +22,5 @@ sealed interface SelectUsersEvents { data class UpdateSearchQuery(val query: String) : SelectUsersEvents data class AddToSelection(val matrixUser: MatrixUser) : SelectUsersEvents data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectUsersEvents + data class OnSearchActiveChanged(val active: Boolean) : SelectUsersEvents } diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 5d89010399..82951b5c09 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -23,5 +23,6 @@ data class SelectUsersState( val searchQuery: String, val searchResults: ImmutableList, val selectedUsers: ImmutableList, + val isSearchActive: Boolean, val eventSink: (SelectUsersEvents) -> Unit, ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 6f0c71a0d3..38b52ec117 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -17,7 +17,6 @@ package io.element.android.features.selectusers.api import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.persistentListOf @@ -25,24 +24,38 @@ import kotlinx.collections.immutable.persistentListOf open class SelectUsersStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( + // TODO add states with selectedUsers aSelectUsersState(), + aSelectUsersState().copy(isSearchActive = true), + aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"), aSelectUsersState().copy( - selectedUsers = persistentListOf( - aMatrixUser(userName = ""), - aMatrixUser(userName = "User"), - aMatrixUser(userName = "User with long name"), + isSearchActive = true, + searchQuery = "@someone:matrix.org", + searchResults = persistentListOf( + MatrixUser(id = UserId("@someone:matrix.org")), + MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), + MatrixUser( + id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"), + username = "hey, I am someone with a very long display name" + ), + MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"), + MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"), + MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"), + MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"), + MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"), + MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"), + MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"), + MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"), + MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"), ) - ) + ), ) } fun aSelectUsersState() = SelectUsersState( + isSearchActive = false, searchQuery = "", searchResults = persistentListOf(), selectedUsers = persistentListOf(), eventSink = {} ) - -fun aMatrixUser(userName: String): MatrixUser { - return MatrixUser(id = UserId("@id"), username = userName, avatarData = AvatarData("@id", "U")) -} diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index f49b7984f8..26e647dc3a 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -22,11 +22,12 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn 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.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 @@ -35,10 +36,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SearchBarDefaults import androidx.compose.runtime.Composable -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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -55,9 +52,9 @@ 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.DockedSearchBar 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.MatrixUserRow import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -70,14 +67,9 @@ import io.element.android.libraries.ui.strings.R as StringR fun SelectUsersView( state: SelectUsersState, modifier: Modifier = Modifier, - onSearchActiveChanged: (Boolean) -> Unit = {}, - onSelectionChanged: (ImmutableList) -> Unit = {}, ) { - var isSearchActive by rememberSaveable { mutableStateOf(false) } val eventSink = state.eventSink - // TODO how to pass back the selection list? - Column( modifier = modifier .fillMaxSize() @@ -86,11 +78,8 @@ fun SelectUsersView( modifier = Modifier.fillMaxWidth(), query = state.searchQuery, results = state.searchResults, - active = isSearchActive, - onActiveChanged = { - isSearchActive = it - onSearchActiveChanged(it) - }, + active = state.isSearchActive, + onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) } ) @@ -133,7 +122,7 @@ fun SelectedUser( Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56))) + Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56.dp))) Text( text = matrixUser.getBestName(), overflow = TextOverflow.Ellipsis, @@ -177,7 +166,7 @@ fun SearchUserBar( focusManager.clearFocus() } - DockedSearchBar( + SearchBar( query = query, onQueryChange = onTextChanged, onSearch = { focusManager.clearFocus() }, @@ -193,7 +182,9 @@ fun SearchUserBar( }, leadingIcon = if (active) { { BackButton(onClick = { onActiveChanged(false) }) } - } else null, + } else { + null + }, trailingIcon = when { active && query.isNotEmpty() -> { { @@ -213,14 +204,15 @@ fun SearchUserBar( } else -> null }, - shape = if (!active) SearchBarDefaults.dockedShape else SearchBarDefaults.fullScreenShape, colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), content = { - results.forEach { - SearchUserResultItem( - matrixUser = it, - onClick = { onResultSelected(it) } - ) + LazyColumn { + items(results) { + SearchUserResultItem( + matrixUser = it, + onClick = { onResultSelected(it) } + ) + } } }, ) @@ -233,9 +225,9 @@ fun SearchUserResultItem( onClick: () -> Unit = {}, ) { MatrixUserRow( - modifier = modifier.heightIn(min = 56.dp), + modifier = modifier, matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36), + avatarSize = AvatarSize.Custom(36.dp), onClick = onClick, ) } diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 12bc44c7e9..84f1d7a950 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -58,11 +58,13 @@ class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { } LaunchedEffect(searchQuery) { + // Clear the search results before performing the search, manually add a fake result with the matrixId, if any searchResults.value = if (MatrixPatterns.isUserId(searchQuery)) { persistentListOf(MatrixUser(UserId(searchQuery))) } else { persistentListOf() } + // Perform the search asynchronously if (searchQuery.isNotEmpty()) { searchResults.value = performSearch(searchQuery) } diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt new file mode 100644 index 0000000000..f00fe56d1c --- /dev/null +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.impl + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.selectusers.api.SelectUsersEvents +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class DefaultSelectUsersPresenterTests { + + @Test + fun `present - initial state`() = runTest { + val presenter = DefaultSelectUsersPresenter() + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState) + } + } + + @Test + fun `present - update search query`() = runTest { + val presenter = DefaultSelectUsersPresenter() + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val matrixIdQuery = "@name:matrix.org" + initialState.eventSink(SelectUsersEvents.UpdateSearchQuery(matrixIdQuery)) + assertThat(awaitItem().searchQuery).isEqualTo(matrixIdQuery) + assertThat(awaitItem().searchResults).containsExactly(MatrixUser(UserId(matrixIdQuery))) + + val notMatrixIdQuery = "name" + initialState.eventSink(SelectUsersEvents.UpdateSearchQuery(notMatrixIdQuery)) + assertThat(awaitItem().searchQuery).isEqualTo(notMatrixIdQuery) + assertThat(awaitItem().searchResults).isEmpty() + } + } +} From 55e91b666f0875df4f8454ead2c162e5552b30d0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 09:59:57 +0100 Subject: [PATCH 13/47] Add missing test cases --- .../selectusers/impl/DefaultSelectUsersPresenterTests.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index f00fe56d1c..7632f1695a 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -48,6 +48,10 @@ class DefaultSelectUsersPresenterTests { presenter.present() }.test { val initialState = awaitItem() + + initialState.eventSink(SelectUsersEvents.OnSearchActiveChanged(true)) + assertThat(awaitItem().isSearchActive).isTrue() + val matrixIdQuery = "@name:matrix.org" initialState.eventSink(SelectUsersEvents.UpdateSearchQuery(matrixIdQuery)) assertThat(awaitItem().searchQuery).isEqualTo(matrixIdQuery) @@ -57,6 +61,9 @@ class DefaultSelectUsersPresenterTests { initialState.eventSink(SelectUsersEvents.UpdateSearchQuery(notMatrixIdQuery)) assertThat(awaitItem().searchQuery).isEqualTo(notMatrixIdQuery) assertThat(awaitItem().searchResults).isEmpty() + + initialState.eventSink(SelectUsersEvents.OnSearchActiveChanged(false)) + assertThat(awaitItem().isSearchActive).isFalse() } } } From 24a179eb5cdb3e2cebb16176f2b802600b0a7f06 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:18:33 +0100 Subject: [PATCH 14/47] Handle missing isSearchActive --- .../features/selectusers/impl/DefaultSelectUsersPresenter.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 84f1d7a950..a0837df242 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -37,12 +37,12 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject -// TODO add unit tests @ContributesBinding(SessionScope::class) class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { @Composable override fun present(): SelectUsersState { + var isSearchActive by rememberSaveable { mutableStateOf(false) } val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { @@ -51,6 +51,7 @@ class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { fun handleEvents(event: SelectUsersEvents) { when (event) { + is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() @@ -74,6 +75,7 @@ class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { searchQuery = searchQuery, searchResults = searchResults.value, selectedUsers = selectedUsers.value, + isSearchActive = isSearchActive, eventSink = ::handleEvents, ) } From 2bdd528842f4332da4f024bd162fa62f7d666ee0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:22:14 +0100 Subject: [PATCH 15/47] Use new selectusers module --- features/createroom/impl/build.gradle.kts | 5 +- .../impl/root/CreateRoomRootEvents.kt | 2 - .../impl/root/CreateRoomRootPresenter.kt | 53 ++------- .../impl/root/CreateRoomRootState.kt | 6 +- .../impl/root/CreateRoomRootStateProvider.kt | 34 ++---- .../impl/root/CreateRoomRootView.kt | 101 ++---------------- .../impl/root/CreateRoomRootPresenterTests.kt | 37 ++----- .../kotlin/extension/DependencyHandleScope.kt | 2 + 8 files changed, 34 insertions(+), 206 deletions(-) diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index dc7eaded75..366cc1e0bd 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -38,9 +38,8 @@ anvil { } dependencies { - anvil(projects.anvilcodegen) implementation(projects.anvilannotations) - + anvil(projects.anvilcodegen) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) @@ -48,6 +47,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) + implementation(projects.features.selectusers.api) api(projects.features.createroom.api) testImplementation(libs.test.junit) @@ -56,6 +56,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.features.selectusers.impl) androidTestImplementation(libs.test.junitext) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt index b9e508032a..5d2f0f684c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootEvents.kt @@ -19,8 +19,6 @@ package io.element.android.features.createroom.impl.root import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface CreateRoomRootEvents { - data class UpdateSearchQuery(val query: String) : CreateRoomRootEvents data class StartDM(val matrixUser: MatrixUser) : CreateRoomRootEvents object InvitePeople : CreateRoomRootEvents - data class OnSearchActiveChanged(val active: Boolean) : CreateRoomRootEvents } 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 23c8d8eaf3..c9fd95339c 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 @@ -17,74 +17,33 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.matrix.api.core.MatrixPatterns -import io.element.android.libraries.matrix.api.core.UserId 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 timber.log.Timber import javax.inject.Inject -class CreateRoomRootPresenter @Inject constructor() : Presenter { +class CreateRoomRootPresenter @Inject constructor( + private val selectUsersPresenter: SelectUsersPresenter, +) : Presenter { @Composable override fun present(): CreateRoomRootState { - var isSearchActive by rememberSaveable { mutableStateOf(false) } - var searchQuery by rememberSaveable { mutableStateOf("") } - val searchResults: MutableState> = remember { - mutableStateOf(persistentListOf()) - } + val selectUsersState = selectUsersPresenter.present() fun handleEvents(event: CreateRoomRootEvents) { when (event) { - is CreateRoomRootEvents.OnSearchActiveChanged -> isSearchActive = event.active - is CreateRoomRootEvents.UpdateSearchQuery -> searchQuery = event.query is CreateRoomRootEvents.StartDM -> handleStartDM(event.matrixUser) CreateRoomRootEvents.InvitePeople -> Unit // Todo Handle invite people action } } - LaunchedEffect(searchQuery) { - // Clear the search results before performing the search, manually add a fake result with the matrixId, if any - searchResults.value = if (MatrixPatterns.isUserId(searchQuery)) { - persistentListOf(MatrixUser(UserId(searchQuery))) - } else { - persistentListOf() - } - // Perform the search asynchronously - if (searchQuery.isNotEmpty()) { - searchResults.value = performSearch(searchQuery) - } - } - return CreateRoomRootState( + selectUsersState = selectUsersState, eventSink = ::handleEvents, - isSearchActive = isSearchActive, - searchQuery = searchQuery, - searchResults = searchResults.value, ) } - private fun performSearch(query: String): ImmutableList { - val isMatrixId = MatrixPatterns.isUserId(query) - val results = mutableListOf()// TODO trigger /search request - if (isMatrixId && results.none { it.id.value == query }) { - val getProfileResult: MatrixUser? = null // TODO trigger /profile request - val profile = getProfileResult ?: MatrixUser(UserId(query)) - results.add(0, profile) - } - return results.toImmutableList() - } - private fun handleStartDM(matrixUser: MatrixUser) { Timber.d("handleStartDM: $matrixUser") // Todo handle start DM action } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt index be9757f122..b7be3f44fb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt @@ -16,13 +16,11 @@ package io.element.android.features.createroom.impl.root +import io.element.android.features.selectusers.api.SelectUsersState import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList -// Do not use default value, so no member get forgotten in the presenters. data class CreateRoomRootState( + val selectUsersState: SelectUsersState, val eventSink: (CreateRoomRootEvents) -> Unit, - val isSearchActive: Boolean, - val searchQuery: String, - val searchResults: ImmutableList, ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt index 5f050ab1cc..e129c98fd4 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.createroom.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.selectusers.api.SelectUsersState import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.persistentListOf @@ -25,35 +26,16 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider get() = sequenceOf( aCreateRoomRootState(), - aCreateRoomRootState().copy(isSearchActive = true), - aCreateRoomRootState().copy(isSearchActive = true, searchQuery = "someone"), - aCreateRoomRootState().copy( - isSearchActive = true, - searchQuery = "@someone:matrix.org", - searchResults = persistentListOf( - MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), - MatrixUser( - id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"), - username = "hey, I am someone with a very long display name" - ), - MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"), - MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"), - MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"), - MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"), - MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"), - MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"), - MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"), - MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"), - MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"), - ) - ), ) } fun aCreateRoomRootState() = CreateRoomRootState( eventSink = {}, - isSearchActive = false, - searchQuery = "", - searchResults = persistentListOf(), + selectUsersState = SelectUsersState( + searchQuery = "", + searchResults = persistentListOf(), + selectedUsers = persistentListOf(), + isSearchActive = false, + eventSink = {}, + ) ) 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 5417ca82da..faa2f3eb68 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 @@ -43,6 +43,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.element.android.features.selectusers.api.SearchUserBar +import io.element.android.features.selectusers.api.SelectUsersView import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -70,7 +72,7 @@ fun CreateRoomRootView( Scaffold( modifier = modifier.fillMaxWidth(), topBar = { - if (!state.isSearchActive) { + if (!state.selectUsersState.isSearchActive) { CreateRoomRootViewTopBar(onClosePressed = onClosePressed) } } @@ -79,18 +81,12 @@ fun CreateRoomRootView( modifier = Modifier.padding(paddingValues), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - CreateRoomSearchBar( + SelectUsersView( modifier = Modifier.fillMaxWidth(), - query = state.searchQuery, - placeHolderTitle = stringResource(StringR.string.search_for_someone), - results = state.searchResults, - active = state.isSearchActive, - onActiveChanged = { state.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(it)) }, - onTextChanged = { state.eventSink(CreateRoomRootEvents.UpdateSearchQuery(it)) }, - onResultSelected = { state.eventSink(CreateRoomRootEvents.StartDM(it)) } + state = state.selectUsersState ) - if (!state.isSearchActive) { + if (!state.selectUsersState.isSearchActive) { CreateRoomActionButtonsList( onNewRoomClicked = onNewRoomClicked, onInvitePeopleClicked = { state.eventSink(CreateRoomRootEvents.InvitePeople) }, @@ -123,77 +119,6 @@ fun CreateRoomRootViewTopBar( ) } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CreateRoomSearchBar( - query: String, - placeHolderTitle: String, - results: ImmutableList, - active: Boolean, - modifier: Modifier = Modifier, - onActiveChanged: (Boolean) -> Unit = {}, - onTextChanged: (String) -> Unit = {}, - onResultSelected: (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.a11y_clear)) - } - } - } - !active -> { - { - Icon( - imageVector = Icons.Default.Search, - contentDescription = stringResource(StringR.string.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 = { - LazyColumn { - items(results) { - CreateRoomSearchResultItem( - matrixUser = it, - onClick = { onResultSelected(it) } - ) - } - } - }, - ) -} - @Composable fun CreateRoomActionButtonsList( modifier: Modifier = Modifier, @@ -214,20 +139,6 @@ fun CreateRoomActionButtonsList( } } -@Composable -fun CreateRoomSearchResultItem( - matrixUser: MatrixUser, - modifier: Modifier = Modifier, - onClick: () -> Unit = {}, -) { - MatrixUserRow( - modifier = modifier, - matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36.dp), - onClick = onClick, - ) -} - @Composable fun CreateRoomActionButton( @DrawableRes iconRes: Int, 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 c13d59f3a6..6a852f4599 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.selectusers.impl.DefaultSelectUsersPresenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -32,7 +33,8 @@ class CreateRoomRootPresenterTests { @Test fun `present - initial state`() = runTest { - val presenter = CreateRoomRootPresenter() + val selectUsersPresenter = DefaultSelectUsersPresenter() + val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -43,45 +45,20 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger action buttons`() = runTest { - val presenter = CreateRoomRootPresenter() + val selectUsersPresenter = DefaultSelectUsersPresenter() + val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(CreateRoomRootEvents.CreateRoom) // Not implemented yet initialState.eventSink(CreateRoomRootEvents.InvitePeople) // Not implemented yet } } - @Test - fun `present - update search query`() = runTest { - val presenter = CreateRoomRootPresenter() - moleculeFlow(RecompositionClock.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - - initialState.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(true)) - assertThat(awaitItem().isSearchActive).isTrue() - - val matrixIdQuery = "@name:matrix.org" - initialState.eventSink(CreateRoomRootEvents.UpdateSearchQuery(matrixIdQuery)) - assertThat(awaitItem().searchQuery).isEqualTo(matrixIdQuery) - assertThat(awaitItem().searchResults).containsExactly(MatrixUser(UserId(matrixIdQuery))) - - val notMatrixIdQuery = "name" - initialState.eventSink(CreateRoomRootEvents.UpdateSearchQuery(notMatrixIdQuery)) - assertThat(awaitItem().searchQuery).isEqualTo(notMatrixIdQuery) - assertThat(awaitItem().searchResults).isEmpty() - - initialState.eventSink(CreateRoomRootEvents.OnSearchActiveChanged(false)) - assertThat(awaitItem().isSearchActive).isFalse() - } - } - @Test fun `present - trigger start DM action`() = runTest { - val presenter = CreateRoomRootPresenter() + val selectUsersPresenter = DefaultSelectUsersPresenter() + val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 52063dd08e..7ae1a0bd15 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -80,6 +80,7 @@ fun DependencyHandlerScope.allFeaturesApi() { implementation(project(":features:preferences:api")) implementation(project(":features:createroom:api")) implementation(project(":features:verifysession:api")) + implementation(project(":features:selectusers:api")) } fun DependencyHandlerScope.allFeaturesImpl() { @@ -92,4 +93,5 @@ fun DependencyHandlerScope.allFeaturesImpl() { implementation(project(":features:preferences:impl")) implementation(project(":features:createroom:impl")) implementation(project(":features:verifysession:impl")) + implementation(project(":features:selectusers:impl")) } From f536c6308d8171e0beee3e9dfeba11b4bcbb8536 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:12:29 +0100 Subject: [PATCH 16/47] Adding multi/single selection variants --- .../impl/root/CreateRoomRootPresenter.kt | 6 +- .../impl/root/CreateRoomRootStateProvider.kt | 13 +- .../impl/root/CreateRoomRootView.kt | 15 +- .../api/SelectMultipleUsersPresenter.kt | 21 +++ ...senter.kt => SelectSingleUserPresenter.kt} | 2 +- .../selectusers/api/SelectUsersState.kt | 1 + .../api/SelectUsersStateProvider.kt | 52 ++++--- .../selectusers/api/SelectUsersView.kt | 140 +++++++++--------- .../DefaultSelectMultipleUsersPresenter.kt | 33 +++++ .../impl/DefaultSelectSingleUserPresenter.kt | 32 ++++ .../impl/DefaultSelectUsersPresenter.kt | 15 +- ... DefaultSelectSingleUserPresenterTests.kt} | 6 +- 12 files changed, 211 insertions(+), 125 deletions(-) create mode 100644 features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt rename features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/{SelectUsersPresenter.kt => SelectSingleUserPresenter.kt} (91%) create mode 100644 features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt create mode 100644 features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt rename features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/{DefaultSelectUsersPresenterTests.kt => DefaultSelectSingleUserPresenterTests.kt} (93%) 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 c9fd95339c..9c29bbf576 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 @@ -17,19 +17,19 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable -import io.element.android.features.selectusers.api.SelectUsersPresenter +import io.element.android.features.selectusers.api.SelectSingleUserPresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.ui.model.MatrixUser import timber.log.Timber import javax.inject.Inject class CreateRoomRootPresenter @Inject constructor( - private val selectUsersPresenter: SelectUsersPresenter, + private val selectSingleUserPresenter: SelectSingleUserPresenter, ) : Presenter { @Composable override fun present(): CreateRoomRootState { - val selectUsersState = selectUsersPresenter.present() + val selectUsersState = selectSingleUserPresenter.present() fun handleEvents(event: CreateRoomRootEvents) { when (event) { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt index e129c98fd4..678f02476c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt @@ -17,10 +17,7 @@ package io.element.android.features.createroom.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.selectusers.api.SelectUsersState -import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.persistentListOf +import io.element.android.features.selectusers.api.aSelectUsersState open class CreateRoomRootStateProvider : PreviewParameterProvider { override val values: Sequence @@ -31,11 +28,5 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt similarity index 91% rename from features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt index de889c80b3..a0755e4375 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt @@ -18,4 +18,4 @@ package io.element.android.features.selectusers.api import io.element.android.libraries.architecture.Presenter -interface SelectUsersPresenter : Presenter +interface SelectSingleUserPresenter : Presenter diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 82951b5c09..4be92a0003 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -24,5 +24,6 @@ data class SelectUsersState( val searchResults: ImmutableList, val selectedUsers: ImmutableList, val isSearchActive: Boolean, + val isMultiSelectionEnabled: Boolean, val eventSink: (SelectUsersEvents) -> Unit, ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 38b52ec117..0b57957afa 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -24,31 +24,23 @@ import kotlinx.collections.immutable.persistentListOf open class SelectUsersStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - // TODO add states with selectedUsers aSelectUsersState(), aSelectUsersState().copy(isSearchActive = true), aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"), + aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone", isMultiSelectionEnabled = true), aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", - searchResults = persistentListOf( - MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), - MatrixUser( - id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"), - username = "hey, I am someone with a very long display name" - ), - MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"), - MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"), - MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"), - MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"), - MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"), - MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"), - MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"), - MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"), - MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"), - ) + selectedUsers = aListOfSelectedUsers(), + searchResults = aListOfResults(), ), + aSelectUsersState().copy( + isSearchActive = true, + searchQuery = "@someone:matrix.org", + isMultiSelectionEnabled = true, + selectedUsers = aListOfSelectedUsers(), + searchResults = aListOfResults(), + ) ) } @@ -57,5 +49,29 @@ fun aSelectUsersState() = SelectUsersState( searchQuery = "", searchResults = persistentListOf(), selectedUsers = persistentListOf(), + isMultiSelectionEnabled = false, eventSink = {} ) + +fun aListOfSelectedUsers() = persistentListOf( + MatrixUser(id = UserId("@someone:matrix.org")), + MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), +) + +fun aListOfResults() = persistentListOf( + MatrixUser(id = UserId("@someone:matrix.org")), + MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), + MatrixUser( + id = UserId("@someone_with_a_very_long_matrix_identifier:a_very_long_domain.org"), + username = "hey, I am someone with a very long display name" + ), + MatrixUser(id = UserId("@someone_2:matrix.org"), username = "someone 2"), + MatrixUser(id = UserId("@someone_3:matrix.org"), username = "someone 3"), + MatrixUser(id = UserId("@someone_4:matrix.org"), username = "someone 4"), + MatrixUser(id = UserId("@someone_5:matrix.org"), username = "someone 5"), + MatrixUser(id = UserId("@someone_6:matrix.org"), username = "someone 6"), + MatrixUser(id = UserId("@someone_7:matrix.org"), username = "someone 7"), + MatrixUser(id = UserId("@someone_8:matrix.org"), username = "someone 8"), + MatrixUser(id = UserId("@someone_9:matrix.org"), username = "someone 9"), + MatrixUser(id = UserId("@someone_10:matrix.org"), username = "someone 10"), +) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 26e647dc3a..53c1c21e02 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -62,7 +61,6 @@ import io.element.android.libraries.matrix.ui.model.getBestName import kotlinx.collections.immutable.ImmutableList import io.element.android.libraries.ui.strings.R as StringR -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SelectUsersView( state: SelectUsersState, @@ -71,79 +69,20 @@ fun SelectUsersView( val eventSink = state.eventSink Column( - modifier = modifier - .fillMaxSize() + modifier = modifier, ) { SearchUserBar( modifier = Modifier.fillMaxWidth(), query = state.searchQuery, results = state.searchResults, + selectedUsers = state.selectedUsers, active = state.isSearchActive, + isMultiSelectionEnabled = state.isMultiSelectionEnabled, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, - onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) } + onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) }, + onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) }, ) - - // TODO move into search content - SelectedUsersList( - modifier = Modifier.padding(16.dp), - selectedUsers = state.selectedUsers, - onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) } - ) - } -} - -@Composable -fun SelectedUsersList( - selectedUsers: List, - modifier: Modifier = Modifier, - onUserRemoved: (MatrixUser) -> Unit = {}, -) { - LazyRow( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - items(selectedUsers) { matrixUser -> - SelectedUser( - matrixUser = matrixUser, - onUserRemoved = onUserRemoved, - ) - } - } -} - -@Composable -fun SelectedUser( - matrixUser: MatrixUser, - modifier: Modifier = Modifier, - onUserRemoved: (MatrixUser) -> Unit, -) { - Box(modifier = modifier.width(56.dp)) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56.dp))) - Text( - text = matrixUser.getBestName(), - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = MaterialTheme.typography.bodyLarge, - ) - } - IconButton( - modifier = Modifier - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .size(20.dp) - .align(Alignment.TopEnd), - onClick = { onUserRemoved(matrixUser) } - ) { - Icon( - imageVector = Icons.Default.Close, - contentDescription = stringResource(id = StringR.string.action_remove), - tint = MaterialTheme.colorScheme.onPrimary, - ) - } } } @@ -152,12 +91,15 @@ fun SelectedUser( fun SearchUserBar( query: String, results: ImmutableList, + selectedUsers: ImmutableList, active: Boolean, + isMultiSelectionEnabled: Boolean, modifier: Modifier = Modifier, placeHolderTitle: String = stringResource(StringR.string.search_for_someone), onActiveChanged: (Boolean) -> Unit = {}, onTextChanged: (String) -> Unit = {}, onResultSelected: (MatrixUser) -> Unit = {}, + onUserRemoved: (MatrixUser) -> Unit = {}, ) { val focusManager = LocalFocusManager.current @@ -206,6 +148,14 @@ fun SearchUserBar( }, colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), content = { + if (isMultiSelectionEnabled && selectedUsers.isNotEmpty()) { + SelectedUsersList( + modifier = Modifier.padding(16.dp), + selectedUsers = selectedUsers, + onUserRemoved = onUserRemoved, + ) + } + LazyColumn { items(results) { SearchUserResultItem( @@ -232,14 +182,68 @@ fun SearchUserResultItem( ) } +@Composable +fun SelectedUsersList( + selectedUsers: List, + modifier: Modifier = Modifier, + onUserRemoved: (MatrixUser) -> Unit = {}, +) { + LazyRow( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + items(selectedUsers) { matrixUser -> + SelectedUser( + matrixUser = matrixUser, + onUserRemoved = onUserRemoved, + ) + } + } +} + +@Composable +fun SelectedUser( + matrixUser: MatrixUser, + modifier: Modifier = Modifier, + onUserRemoved: (MatrixUser) -> Unit, +) { + Box(modifier = modifier.width(56.dp)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Avatar(matrixUser.avatarData.copy(size = AvatarSize.Custom(56.dp))) + Text( + text = matrixUser.getBestName(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = MaterialTheme.typography.bodyLarge, + ) + } + IconButton( + modifier = Modifier + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .size(20.dp) + .align(Alignment.TopEnd), + onClick = { onUserRemoved(matrixUser) } + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = stringResource(id = StringR.string.action_remove), + tint = MaterialTheme.colorScheme.onPrimary, + ) + } + } +} + @Preview @Composable -internal fun ChangeServerViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = +internal fun SelectUsersViewLightPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun ChangeServerViewDarkPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = +internal fun SelectUsersViewDarkPreview(@PreviewParameter(SelectUsersStateProvider::class) state: SelectUsersState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt new file mode 100644 index 0000000000..7e6628e878 --- /dev/null +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.selectusers.api.SelectMultipleUsersPresenter +import io.element.android.libraries.di.SessionScope +import javax.inject.Inject + +// TODO add unit tests +@ContributesBinding( + scope = SessionScope::class, + boundType = SelectMultipleUsersPresenter::class, +) +class DefaultSelectMultipleUsersPresenter @Inject constructor() : + DefaultSelectUsersPresenter, + SelectMultipleUsersPresenter { + override val isMultiSelectionEnabled: Boolean = true +} diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt new file mode 100644 index 0000000000..e4fb82acb4 --- /dev/null +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.selectusers.api.SelectSingleUserPresenter +import io.element.android.libraries.di.SessionScope +import javax.inject.Inject + +@ContributesBinding( + scope = SessionScope::class, + boundType = SelectSingleUserPresenter::class, +) +class DefaultSelectSingleUserPresenter @Inject constructor() : + DefaultSelectUsersPresenter, + SelectSingleUserPresenter { + override val isMultiSelectionEnabled: Boolean = false +} diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index a0837df242..69cec08433 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -24,26 +24,26 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.selectusers.api.SelectUsersEvents -import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.features.selectusers.api.SelectUsersState -import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId 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 javax.inject.Inject -@ContributesBinding(SessionScope::class) -class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { +interface DefaultSelectUsersPresenter : Presenter { + + val isMultiSelectionEnabled: Boolean @Composable override fun present(): SelectUsersState { var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf()) } + val selectedUsers: MutableState> = remember { + mutableStateOf(persistentListOf()) + } var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { mutableStateOf(persistentListOf()) @@ -76,6 +76,7 @@ class DefaultSelectUsersPresenter @Inject constructor() : SelectUsersPresenter { searchResults = searchResults.value, selectedUsers = selectedUsers.value, isSearchActive = isSearchActive, + isMultiSelectionEnabled = isMultiSelectionEnabled, eventSink = ::handleEvents, ) } diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt similarity index 93% rename from features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt rename to features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt index 7632f1695a..61c4a386ec 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt @@ -28,11 +28,11 @@ import kotlinx.coroutines.test.runTest import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) -class DefaultSelectUsersPresenterTests { +class DefaultSelectSingleUserPresenterTests { @Test fun `present - initial state`() = runTest { - val presenter = DefaultSelectUsersPresenter() + val presenter = DefaultSelectSingleUserPresenter() moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -43,7 +43,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - update search query`() = runTest { - val presenter = DefaultSelectUsersPresenter() + val presenter = DefaultSelectSingleUserPresenter() moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { From d0158c06dcc9919cfe8952cf17d64ee6c10fae92 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:21:00 +0100 Subject: [PATCH 17/47] Adding callbacks on the SelectUsersView --- .../impl/root/CreateRoomRootView.kt | 1 + .../selectusers/api/SelectUsersView.kt | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) 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 46202cb691..b0791c5331 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 @@ -71,6 +71,7 @@ fun CreateRoomRootView( SelectUsersView( modifier = Modifier.fillMaxWidth(), state = state.selectUsersState, + onUserSelected = { state.eventSink.invoke(CreateRoomRootEvents.StartDM(it)) }, ) if (!state.selectUsersState.isSearchActive) { diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 53c1c21e02..5dc17b717a 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -65,6 +65,8 @@ import io.element.android.libraries.ui.strings.R as StringR fun SelectUsersView( state: SelectUsersState, modifier: Modifier = Modifier, + onUserSelected: (MatrixUser) -> Unit = {}, + onUserDeselected: (MatrixUser) -> Unit = {}, ) { val eventSink = state.eventSink @@ -80,8 +82,14 @@ fun SelectUsersView( isMultiSelectionEnabled = state.isMultiSelectionEnabled, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, - onResultSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) }, - onUserRemoved = { eventSink(SelectUsersEvents.RemoveFromSelection(it)) }, + onUserSelected = { + state.eventSink(SelectUsersEvents.AddToSelection(it)) + onUserSelected(it) + }, + onUserDeselected = { + eventSink(SelectUsersEvents.RemoveFromSelection(it)) + onUserDeselected(it) + }, ) } } @@ -98,8 +106,8 @@ fun SearchUserBar( placeHolderTitle: String = stringResource(StringR.string.search_for_someone), onActiveChanged: (Boolean) -> Unit = {}, onTextChanged: (String) -> Unit = {}, - onResultSelected: (MatrixUser) -> Unit = {}, - onUserRemoved: (MatrixUser) -> Unit = {}, + onUserSelected: (MatrixUser) -> Unit = {}, + onUserDeselected: (MatrixUser) -> Unit = {}, ) { val focusManager = LocalFocusManager.current @@ -152,7 +160,7 @@ fun SearchUserBar( SelectedUsersList( modifier = Modifier.padding(16.dp), selectedUsers = selectedUsers, - onUserRemoved = onUserRemoved, + onUserRemoved = onUserDeselected, ) } @@ -160,7 +168,7 @@ fun SearchUserBar( items(results) { SearchUserResultItem( matrixUser = it, - onClick = { onResultSelected(it) } + onClick = { onUserSelected(it) } ) } } From f3c4c828b252b84c491351745dae349966779c95 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:26:07 +0100 Subject: [PATCH 18/47] Using an immutable set for the selected users --- .../features/selectusers/api/SelectUsersState.kt | 3 ++- .../selectusers/api/SelectUsersStateProvider.kt | 9 +++++---- .../features/selectusers/api/SelectUsersView.kt | 7 ++++--- .../selectusers/impl/DefaultSelectUsersPresenter.kt | 11 +++++++---- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 4be92a0003..6d1b1e5757 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -18,11 +18,12 @@ package io.element.android.features.selectusers.api import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableSet data class SelectUsersState( val searchQuery: String, val searchResults: ImmutableList, - val selectedUsers: ImmutableList, + val selectedUsers: ImmutableSet, val isSearchActive: Boolean, val isMultiSelectionEnabled: Boolean, val eventSink: (SelectUsersEvents) -> Unit, diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 0b57957afa..6bc7c40b1e 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -20,6 +20,7 @@ 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 kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf open class SelectUsersStateProvider : PreviewParameterProvider { override val values: Sequence @@ -31,14 +32,14 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", - selectedUsers = aListOfSelectedUsers(), + selectedUsers = aSetOfSelectedUsers(), searchResults = aListOfResults(), ), aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", isMultiSelectionEnabled = true, - selectedUsers = aListOfSelectedUsers(), + selectedUsers = aSetOfSelectedUsers(), searchResults = aListOfResults(), ) ) @@ -48,12 +49,12 @@ fun aSelectUsersState() = SelectUsersState( isSearchActive = false, searchQuery = "", searchResults = persistentListOf(), - selectedUsers = persistentListOf(), + selectedUsers = persistentSetOf(), isMultiSelectionEnabled = false, eventSink = {} ) -fun aListOfSelectedUsers() = persistentListOf( +fun aSetOfSelectedUsers() = persistentSetOf( MatrixUser(id = UserId("@someone:matrix.org")), MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 5dc17b717a..f04ad011de 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -59,6 +59,7 @@ 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 kotlinx.collections.immutable.ImmutableSet import io.element.android.libraries.ui.strings.R as StringR @Composable @@ -99,7 +100,7 @@ fun SelectUsersView( fun SearchUserBar( query: String, results: ImmutableList, - selectedUsers: ImmutableList, + selectedUsers: ImmutableSet, active: Boolean, isMultiSelectionEnabled: Boolean, modifier: Modifier = Modifier, @@ -192,7 +193,7 @@ fun SearchUserResultItem( @Composable fun SelectedUsersList( - selectedUsers: List, + selectedUsers: ImmutableSet, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit = {}, ) { @@ -200,7 +201,7 @@ fun SelectedUsersList( modifier = modifier, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { - items(selectedUsers) { matrixUser -> + items(selectedUsers.toList()) { matrixUser -> SelectedUser( matrixUser = matrixUser, onUserRemoved = onUserRemoved, diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 69cec08433..77e3c6f6c5 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -31,8 +31,11 @@ import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableSet interface DefaultSelectUsersPresenter : Presenter { @@ -41,8 +44,8 @@ interface DefaultSelectUsersPresenter : Presenter { @Composable override fun present(): SelectUsersState { var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers: MutableState> = remember { - mutableStateOf(persistentListOf()) + val selectedUsers: MutableState> = remember { + mutableStateOf(persistentSetOf()) } var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { @@ -53,8 +56,8 @@ interface DefaultSelectUsersPresenter : Presenter { when (event) { is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query - is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() - is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet() + is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableSet() } } From c58311272d9ba731459b2de617d87dbf1dd8baac Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:23:15 +0100 Subject: [PATCH 19/47] Using qualifier annotation for bindings of SelectUsersPresenter --- .../impl/addpeople/AddPeopleEvents.kt | 7 +----- .../impl/addpeople/AddPeopleNode.kt | 4 ++-- .../impl/addpeople/AddPeoplePresenter.kt | 22 ++++++++--------- .../impl/addpeople/AddPeopleState.kt | 5 ++-- .../impl/addpeople/AddPeopleStateProvider.kt | 20 +++++++++------- .../impl/addpeople/AddPeopleView.kt | 14 +++++------ .../impl/root/CreateRoomRootPresenter.kt | 9 ++++--- .../selectusers/api/SelectUsersPresenter.kt | 24 +++++++++++++++++++ .../DefaultSelectMultipleUsersPresenter.kt | 13 +++++----- .../impl/DefaultSelectSingleUserPresenter.kt | 12 +++++----- .../impl/DefaultSelectUsersPresenter.kt | 6 ++--- 11 files changed, 79 insertions(+), 57 deletions(-) create mode 100644 features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.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 index c7ce6308e1..5d246be501 100644 --- 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 @@ -16,9 +16,4 @@ package io.element.android.features.createroom.impl.addpeople -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList - -sealed interface AddPeopleEvents { - data class UpdateSelection(val users: ImmutableList) : AddPeopleEvents -} +sealed interface AddPeopleEvents 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 751ab7c53f..5393075d18 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 @@ -24,9 +24,9 @@ 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.di.AppScope +import io.element.android.libraries.di.SessionScope -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::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 4cca64b4dd..23eb9adbec 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,30 +17,28 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import io.element.android.features.selectusers.api.MULTI_SELECTION_USERS_VARIANT +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import javax.inject.Inject +import javax.inject.Named // TODO add unit tests -class AddPeoplePresenter @Inject constructor() : Presenter { +class AddPeoplePresenter @Inject constructor( + @Named(MULTI_SELECTION_USERS_VARIANT) + private val selectUsersPresenter: SelectUsersPresenter, +) : Presenter { @Composable override fun present(): AddPeopleState { - val selectedUsers: MutableState> = remember { mutableStateOf(persistentListOf(aMatrixUser("test"))) } + val selectUsersState = selectUsersPresenter.present() fun handleEvents(event: AddPeopleEvents) { - when (event) { - is AddPeopleEvents.UpdateSelection -> selectedUsers.value = event.users - } + // do nothing for now } return AddPeopleState( - selectedUsers = selectedUsers.value, + selectUsersState = selectUsersState, eventSink = ::handleEvents, ) } 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 index 6214e2259a..8212d02cc4 100644 --- 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 @@ -16,10 +16,9 @@ package io.element.android.features.createroom.impl.addpeople -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList +import io.element.android.features.selectusers.api.SelectUsersState data class AddPeopleState( - val selectedUsers: ImmutableList, + val selectUsersState: SelectUsersState, 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 7d7c1851ff..fc4f4d2648 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 @@ -17,27 +17,31 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.selectusers.api.aSelectUsersState +import io.element.android.features.selectusers.api.aSetOfSelectedUsers import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.persistentListOf open class AddPeopleStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAddPeopleState(), aAddPeopleState().copy( - selectedUsers = persistentListOf( - aMatrixUser(userName = ""), - aMatrixUser(userName = "User"), - aMatrixUser(userName = "User with long name"), - ) + selectUsersState = aSelectUsersState().copy(selectedUsers = aSetOfSelectedUsers()) + ), + aAddPeopleState(isSearchActive = true), + aAddPeopleState(isSearchActive = true).copy( + selectUsersState = aSelectUsersState().copy(selectedUsers = aSetOfSelectedUsers()) ) ) } -fun aAddPeopleState() = AddPeopleState( - selectedUsers = persistentListOf(), +fun aAddPeopleState(isSearchActive: Boolean = false) = AddPeopleState( + selectUsersState = aSelectUsersState().copy( + isMultiSelectionEnabled = true, + isSearchActive = isSearchActive, + ), 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 62e935079a..acecb7bb95 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 @@ -18,13 +18,10 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -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.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -32,6 +29,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.element.android.features.selectusers.api.SelectUsersView import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -49,13 +47,12 @@ fun AddPeopleView( onBackPressed: () -> Unit = {}, onNextPressed: () -> Unit = {}, ) { - var isSearchActive by rememberSaveable { mutableStateOf(false) } val eventSink = state.eventSink Scaffold( topBar = { AddPeopleViewTopBar( - hasSelectedUsers = state.selectedUsers.isNotEmpty(), + hasSelectedUsers = state.selectUsersState.selectedUsers.isNotEmpty(), onBackPressed = onBackPressed, onNextPressed = onNextPressed, ) @@ -66,7 +63,10 @@ fun AddPeopleView( .fillMaxSize() .padding(padding), ) { - // TODO use reusable searchUser bar with multi selection + SelectUsersView( + modifier = Modifier.fillMaxWidth(), + state = state.selectUsersState, + ) } } } 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 9c29bbf576..b7bca3a564 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 @@ -17,19 +17,22 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable -import io.element.android.features.selectusers.api.SelectSingleUserPresenter +import io.element.android.features.selectusers.api.SINGLE_SELECTION_USERS_VARIANT +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.ui.model.MatrixUser import timber.log.Timber import javax.inject.Inject +import javax.inject.Named class CreateRoomRootPresenter @Inject constructor( - private val selectSingleUserPresenter: SelectSingleUserPresenter, + @Named(SINGLE_SELECTION_USERS_VARIANT) + private val selectUsersPresenter: SelectUsersPresenter, ) : Presenter { @Composable override fun present(): CreateRoomRootState { - val selectUsersState = selectSingleUserPresenter.present() + val selectUsersState = selectUsersPresenter.present() fun handleEvents(event: CreateRoomRootEvents) { when (event) { diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt new file mode 100644 index 0000000000..913815c698 --- /dev/null +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.selectusers.api + +import io.element.android.libraries.architecture.Presenter + +const val SINGLE_SELECTION_USERS_VARIANT = "single_selection_users" +const val MULTI_SELECTION_USERS_VARIANT = "multi_selection_users" + +interface SelectUsersPresenter : Presenter diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt index 7e6628e878..9cc948d62f 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt @@ -17,17 +17,18 @@ package io.element.android.features.selectusers.impl import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.selectusers.api.SelectMultipleUsersPresenter +import io.element.android.features.selectusers.api.MULTI_SELECTION_USERS_VARIANT +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.libraries.di.SessionScope import javax.inject.Inject +import javax.inject.Named // TODO add unit tests @ContributesBinding( scope = SessionScope::class, - boundType = SelectMultipleUsersPresenter::class, + boundType = SelectUsersPresenter::class ) +@Named(MULTI_SELECTION_USERS_VARIANT) class DefaultSelectMultipleUsersPresenter @Inject constructor() : - DefaultSelectUsersPresenter, - SelectMultipleUsersPresenter { - override val isMultiSelectionEnabled: Boolean = true -} + SelectUsersPresenter by DefaultSelectUsersPresenter(isMultiSelectionEnabled = true) + diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt index e4fb82acb4..ea538e0e39 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt @@ -17,16 +17,16 @@ package io.element.android.features.selectusers.impl import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.selectusers.api.SelectSingleUserPresenter +import io.element.android.features.selectusers.api.SINGLE_SELECTION_USERS_VARIANT +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.libraries.di.SessionScope import javax.inject.Inject +import javax.inject.Named @ContributesBinding( scope = SessionScope::class, - boundType = SelectSingleUserPresenter::class, + boundType = SelectUsersPresenter::class ) +@Named(SINGLE_SELECTION_USERS_VARIANT) class DefaultSelectSingleUserPresenter @Inject constructor() : - DefaultSelectUsersPresenter, - SelectSingleUserPresenter { - override val isMultiSelectionEnabled: Boolean = false -} + SelectUsersPresenter by DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 77e3c6f6c5..ac036fb1c0 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -25,8 +25,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import io.element.android.features.selectusers.api.SelectUsersEvents +import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.features.selectusers.api.SelectUsersState -import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -37,9 +37,7 @@ import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet -interface DefaultSelectUsersPresenter : Presenter { - - val isMultiSelectionEnabled: Boolean +class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter { @Composable override fun present(): SelectUsersState { From bb0edaf8d8610b2dcd982b639c192ea11be281a6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:19:32 +0100 Subject: [PATCH 20/47] Hiding the top bar when search is active --- .../impl/addpeople/AddPeopleStateProvider.kt | 28 ++++++++----------- .../impl/addpeople/AddPeopleView.kt | 12 ++++---- 2 files changed, 19 insertions(+), 21 deletions(-) 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 fc4f4d2648..37428071d7 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 @@ -19,32 +19,28 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.selectusers.api.aSelectUsersState import io.element.android.features.selectusers.api.aSetOfSelectedUsers -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.ui.model.MatrixUser open class AddPeopleStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aAddPeopleState(), aAddPeopleState().copy( - selectUsersState = aSelectUsersState().copy(selectedUsers = aSetOfSelectedUsers()) + selectUsersState = aSelectUsersState().copy( + selectedUsers = aSetOfSelectedUsers(), + isMultiSelectionEnabled = true, + ) ), - aAddPeopleState(isSearchActive = true), - aAddPeopleState(isSearchActive = true).copy( - selectUsersState = aSelectUsersState().copy(selectedUsers = aSetOfSelectedUsers()) + aAddPeopleState().copy( + selectUsersState = aSelectUsersState().copy( + selectedUsers = aSetOfSelectedUsers(), + isSearchActive = true, + isMultiSelectionEnabled = true, + ) ) ) } -fun aAddPeopleState(isSearchActive: Boolean = false) = AddPeopleState( - selectUsersState = aSelectUsersState().copy( - isMultiSelectionEnabled = true, - isSearchActive = isSearchActive, - ), +fun aAddPeopleState() = AddPeopleState( + selectUsersState = aSelectUsersState(), eventSink = {} ) - -fun aMatrixUser(userName: String): MatrixUser { - return MatrixUser(id = UserId("@id"), username = userName, avatarData = AvatarData("@id", "U")) -} 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 acecb7bb95..842256d9c3 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 @@ -51,11 +51,13 @@ fun AddPeopleView( Scaffold( topBar = { - AddPeopleViewTopBar( - hasSelectedUsers = state.selectUsersState.selectedUsers.isNotEmpty(), - onBackPressed = onBackPressed, - onNextPressed = onNextPressed, - ) + if (!state.selectUsersState.isSearchActive) { + AddPeopleViewTopBar( + hasSelectedUsers = state.selectUsersState.selectedUsers.isNotEmpty(), + onBackPressed = onBackPressed, + onNextPressed = onNextPressed, + ) + } } ) { padding -> Column( From 997d4c87a1b3b2f8ff6033c7fb5283031206eb7d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:29:31 +0100 Subject: [PATCH 21/47] Display the list of selected users when search is not active --- .../selectusers/api/SelectUsersStateProvider.kt | 5 +++++ .../features/selectusers/api/SelectUsersView.kt | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 6bc7c40b1e..c8e7d9e6e5 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -26,6 +26,11 @@ open class SelectUsersStateProvider : PreviewParameterProvider override val values: Sequence get() = sequenceOf( aSelectUsersState(), + aSelectUsersState().copy( + isSearchActive = false, + selectedUsers = aSetOfSelectedUsers(), + isMultiSelectionEnabled = true + ), aSelectUsersState().copy(isSearchActive = true), aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"), aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone", isMultiSelectionEnabled = true), diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index f04ad011de..0c29e35f15 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -92,6 +92,17 @@ fun SelectUsersView( onUserDeselected(it) }, ) + + if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { + SelectedUsersList( + modifier = Modifier.padding(16.dp), + selectedUsers = state.selectedUsers, + onUserRemoved = { + eventSink(SelectUsersEvents.RemoveFromSelection(it)) + onUserDeselected(it) + }, + ) + } } } @@ -157,7 +168,7 @@ fun SearchUserBar( }, colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), content = { - if (isMultiSelectionEnabled && selectedUsers.isNotEmpty()) { + if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { SelectedUsersList( modifier = Modifier.padding(16.dp), selectedUsers = selectedUsers, From cee313597bf4501cf0c2d49031e8e71ac3c662f1 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 14:41:03 +0100 Subject: [PATCH 22/47] Adding checkbox in result when multi selection of users is enabled --- .../api/SelectUsersStateProvider.kt | 4 +- .../selectusers/api/SelectUsersView.kt | 47 +++++++++-- .../ui/components/CheckableMatrixUserRow.kt | 83 +++++++++++++++++++ .../matrix/ui/components/MatrixUserRow.kt | 5 +- 4 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index c8e7d9e6e5..59405004fe 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -61,12 +61,12 @@ fun aSelectUsersState() = SelectUsersState( fun aSetOfSelectedUsers() = persistentSetOf( MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), + MatrixUser(id = UserId("@other:matrix.org"), username = "other"), ) fun aListOfResults() = persistentListOf( MatrixUser(id = UserId("@someone:matrix.org")), - MatrixUser(id = UserId("@someone:matrix.org"), username = "someone"), + 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" diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 0c29e35f15..8d2c3ae71d 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -55,6 +55,7 @@ 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 @@ -177,11 +178,29 @@ fun SearchUserBar( } LazyColumn { - items(results) { - SearchUserResultItem( - matrixUser = it, - onClick = { onUserSelected(it) } - ) + 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) } + ) + } } } }, @@ -189,7 +208,23 @@ fun SearchUserBar( } @Composable -fun SearchUserResultItem( +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 = {}, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt new file mode 100644 index 0000000000..67969f8e2e --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt @@ -0,0 +1,83 @@ +/* + * 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.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +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.Checkbox +import io.element.android.libraries.matrix.ui.model.MatrixUser + +@Composable +fun CheckableMatrixUserRow( + checked: Boolean, + matrixUser: MatrixUser, + modifier: Modifier = Modifier, + avatarSize: AvatarSize = matrixUser.avatarData.size, + onCheckedChange: (Boolean) -> Unit = {}, + enabled: Boolean = true, +) { + + Row( + modifier = modifier + .fillMaxWidth() + .clickable { + onCheckedChange(!checked) + }, + verticalAlignment = Alignment.CenterVertically, + ) { + MatrixUserRow( + modifier = Modifier.weight(1f), + matrixUser = matrixUser, + avatarSize = avatarSize, + isClickEnabled = false, + ) + + Checkbox( + checked = checked, + onCheckedChange = onCheckedChange, + enabled = enabled, + ) + } +} + +@Preview +@Composable +internal fun CheckableMatrixUserRowLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = + ElementPreviewLight { ContentToPreview(matrixUser) } + +@Preview +@Composable +internal fun CheckableMatrixUserRowDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = + ElementPreviewDark { ContentToPreview(matrixUser) } + +@Composable +private fun ContentToPreview(matrixUser: MatrixUser) { + Column { + CheckableMatrixUserRow(checked = true, matrixUser) + CheckableMatrixUserRow(checked = false, matrixUser) + } +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index 035f2d3824..768c803850 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -46,12 +45,12 @@ fun MatrixUserRow( matrixUser: MatrixUser, modifier: Modifier = Modifier, avatarSize: AvatarSize = matrixUser.avatarData.size, + isClickEnabled: Boolean = true, onClick: () -> Unit = {}, ) { Row( modifier = modifier - .clickable(onClick = onClick) - .fillMaxWidth() + .let { if (isClickEnabled) it.clickable(onClick = onClick) else it } .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp) .height(IntrinsicSize.Min), verticalAlignment = Alignment.CenterVertically From 790c2137c70a5c19706a27105bc149cc40af5259 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:00:08 +0100 Subject: [PATCH 23/47] Adding unit tests for selection of users --- .../DefaultSelectMultipleUsersPresenter.kt | 1 - ...kt => DefaultSelectUsersPresenterTests.kt} | 48 +++++++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) rename features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/{DefaultSelectSingleUserPresenterTests.kt => DefaultSelectUsersPresenterTests.kt} (54%) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt index 9cc948d62f..f2d6be0124 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt @@ -23,7 +23,6 @@ import io.element.android.libraries.di.SessionScope import javax.inject.Inject import javax.inject.Named -// TODO add unit tests @ContributesBinding( scope = SessionScope::class, boundType = SelectUsersPresenter::class diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt similarity index 54% rename from features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt rename to features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index 61c4a386ec..36db35d61d 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -22,6 +22,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.selectusers.api.SelectUsersEvents 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 import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -31,19 +32,38 @@ import org.junit.Test class DefaultSelectSingleUserPresenterTests { @Test - fun `present - initial state`() = runTest { - val presenter = DefaultSelectSingleUserPresenter() + fun `present - initial state for single selection`() = runTest { + val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState) + assertThat(initialState.searchQuery).isEmpty() + assertThat(initialState.isMultiSelectionEnabled).isFalse() + assertThat(initialState.isSearchActive).isFalse() + assertThat(initialState.selectedUsers).isEmpty() + assertThat(initialState.searchResults).isEmpty() + } + } + + @Test + fun `present - initial state for multiple selection`() = runTest { + val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = true) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.searchQuery).isEmpty() + assertThat(initialState.isMultiSelectionEnabled).isTrue() + assertThat(initialState.isSearchActive).isFalse() + assertThat(initialState.selectedUsers).isEmpty() + assertThat(initialState.searchResults).isEmpty() } } @Test fun `present - update search query`() = runTest { - val presenter = DefaultSelectSingleUserPresenter() + val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -66,4 +86,24 @@ class DefaultSelectSingleUserPresenterTests { assertThat(awaitItem().isSearchActive).isFalse() } } + + @Test + fun `present - select a user`() = runTest { + val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + val matrixUser = aMatrixUser() + initialState.eventSink(SelectUsersEvents.AddToSelection(matrixUser)) + val selectionAfterAdd = awaitItem().selectedUsers + assertThat(selectionAfterAdd).hasSize(1) + assertThat(selectionAfterAdd).contains(matrixUser) + + initialState.eventSink(SelectUsersEvents.RemoveFromSelection(matrixUser)) + val selectionAfterRemove = awaitItem().selectedUsers + assertThat(selectionAfterRemove).isEmpty() + } + } } From ddcde735fed1085834ead57f263648c4f822eb7a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:22:04 +0100 Subject: [PATCH 24/47] Present the last added user first --- .../impl/DefaultSelectUsersPresenter.kt | 2 +- .../impl/DefaultSelectUsersPresenterTests.kt | 30 ++++++++++++++----- .../ui/components/MatrixUserProvider.kt | 6 ++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index ac036fb1c0..f1c8978402 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -75,7 +75,7 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) return SelectUsersState( searchQuery = searchQuery, searchResults = searchResults.value, - selectedUsers = selectedUsers.value, + selectedUsers = selectedUsers.value.reversed().toImmutableSet(), isSearchActive = isSearchActive, isMultiSelectionEnabled = isMultiSelectionEnabled, eventSink = ::handleEvents, diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index 36db35d61d..a00c7bd775 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -95,15 +95,29 @@ class DefaultSelectSingleUserPresenterTests { }.test { val initialState = awaitItem() - val matrixUser = aMatrixUser() - initialState.eventSink(SelectUsersEvents.AddToSelection(matrixUser)) - val selectionAfterAdd = awaitItem().selectedUsers - assertThat(selectionAfterAdd).hasSize(1) - assertThat(selectionAfterAdd).contains(matrixUser) + val userA = aMatrixUser("userA", "A") + val userB = aMatrixUser("userB", "B") + initialState.eventSink(SelectUsersEvents.AddToSelection(userA)) + val selectionAfterAddingA = awaitItem().selectedUsers + assertThat(selectionAfterAddingA).hasSize(1) + assertThat(selectionAfterAddingA).contains(userA) - initialState.eventSink(SelectUsersEvents.RemoveFromSelection(matrixUser)) - val selectionAfterRemove = awaitItem().selectedUsers - assertThat(selectionAfterRemove).isEmpty() + initialState.eventSink(SelectUsersEvents.AddToSelection(userB)) + val selectionAfterAddingB = awaitItem().selectedUsers + assertThat(selectionAfterAddingB).hasSize(2) + assertThat(selectionAfterAddingB).contains(userA) + assertThat(selectionAfterAddingB).contains(userB) + // the last added user should be presented first + assertThat(selectionAfterAddingB.first()).isEqualTo(userB) + + initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userB)) + val selectionAfterRemovingB = awaitItem().selectedUsers + assertThat(selectionAfterRemovingB).hasSize(1) + assertThat(selectionAfterRemovingB).contains(userA) + + initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userA)) + val selectionAfterRemovingA = awaitItem().selectedUsers + assertThat(selectionAfterRemovingA).isEmpty() } } } 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 b19424c28b..b95b93d348 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 @@ -32,9 +32,9 @@ open class MatrixUserProvider : PreviewParameterProvider { ) } -fun aMatrixUser() = MatrixUser( - id = UserId("@id_of_alice:server.org"), - username = "Alice", +fun aMatrixUser(id: String = "@id_of_alice:server.org", userName: String = "Alice") = MatrixUser( + id = UserId(id), + username = userName, avatarData = anAvatarData() ) From 91884081602acf731be1ac72919cd74f3e5c9ab3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:43:06 +0100 Subject: [PATCH 25/47] Scrolling to first item of the selected users list after adding a user --- .../selectusers/api/SelectUsersState.kt | 2 ++ .../selectusers/api/SelectUsersStateProvider.kt | 5 +++++ .../features/selectusers/api/SelectUsersView.kt | 7 +++++++ .../impl/DefaultSelectUsersPresenter.kt | 17 ++++++++++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 6d1b1e5757..86437d2872 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -16,6 +16,7 @@ package io.element.android.features.selectusers.api +import androidx.compose.foundation.lazy.LazyListState import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet @@ -24,6 +25,7 @@ data class SelectUsersState( val searchQuery: String, val searchResults: ImmutableList, val selectedUsers: ImmutableSet, + val selectedUsersListState: LazyListState, val isSearchActive: Boolean, val isMultiSelectionEnabled: Boolean, val eventSink: (SelectUsersEvents) -> Unit, diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 59405004fe..e85b68e986 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -16,6 +16,7 @@ package io.element.android.features.selectusers.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 @@ -55,6 +56,10 @@ fun aSelectUsersState() = SelectUsersState( searchQuery = "", searchResults = persistentListOf(), selectedUsers = persistentSetOf(), + selectedUsersListState = LazyListState( + firstVisibleItemIndex = 0, + firstVisibleItemScrollOffset = 0, + ), isMultiSelectionEnabled = false, eventSink = {} ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 8d2c3ae71d..4e07ebd0ca 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -25,6 +25,7 @@ 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 @@ -80,6 +81,7 @@ fun SelectUsersView( query = state.searchQuery, results = state.searchResults, selectedUsers = state.selectedUsers, + selectedUsersListState = state.selectedUsersListState, active = state.isSearchActive, isMultiSelectionEnabled = state.isMultiSelectionEnabled, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, @@ -96,6 +98,7 @@ fun SelectUsersView( if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { SelectedUsersList( + listState = state.selectedUsersListState, modifier = Modifier.padding(16.dp), selectedUsers = state.selectedUsers, onUserRemoved = { @@ -113,6 +116,7 @@ fun SearchUserBar( query: String, results: ImmutableList, selectedUsers: ImmutableSet, + selectedUsersListState: LazyListState, active: Boolean, isMultiSelectionEnabled: Boolean, modifier: Modifier = Modifier, @@ -171,6 +175,7 @@ fun SearchUserBar( content = { if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { SelectedUsersList( + listState = selectedUsersListState, modifier = Modifier.padding(16.dp), selectedUsers = selectedUsers, onUserRemoved = onUserDeselected, @@ -239,11 +244,13 @@ fun SearchSingleUserResultItem( @Composable fun SelectedUsersList( + listState: LazyListState, selectedUsers: ImmutableSet, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit = {}, ) { LazyRow( + state = listState, modifier = modifier, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index f1c8978402..5e194adcc8 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -16,12 +16,15 @@ package io.element.android.features.selectusers.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 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 io.element.android.features.selectusers.api.SelectUsersEvents @@ -36,15 +39,19 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter { @Composable override fun present(): SelectUsersState { + val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } val selectedUsers: MutableState> = remember { mutableStateOf(persistentSetOf()) } + val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { mutableStateOf(persistentListOf()) @@ -54,7 +61,10 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) when (event) { is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query - is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet() + is SelectUsersEvents.AddToSelection -> { + selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet() + localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) + } is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableSet() } } @@ -76,6 +86,7 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) searchQuery = searchQuery, searchResults = searchResults.value, selectedUsers = selectedUsers.value.reversed().toImmutableSet(), + selectedUsersListState = selectedUsersListState, isSearchActive = isSearchActive, isMultiSelectionEnabled = isMultiSelectionEnabled, eventSink = ::handleEvents, @@ -92,4 +103,8 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) } return results.toImmutableList() } + + private fun CoroutineScope.scrollToFirstSelectedUser(listState: LazyListState) = launch { + listState.scrollToItem(index = 0) + } } From a2786eb4fd8cbea55550a84fec9187ee0e65e979 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:47:53 +0100 Subject: [PATCH 26/47] Updating screenshots tests --- ...CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 --- ...CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 --- ...CreateRoomRootViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 --- ...reateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 --- ...reateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 --- ...reateRoomRootViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 --- ...roup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- 8 files changed, 4 insertions(+), 22 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_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.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index f13a65f8e2..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:89ea65099fb4981bbeb24e49afb7400b0e8da79e3b783e198519ba1e970404a8 -size 8317 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_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.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 74a7f599cd..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a8ba207cf61c56b64c6855d04c8a30def0b3daf325adf57edd45b40981ed745 -size 7733 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_3,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.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 6d7f2c5d11..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6703f2cba99aa60bdf3ecd380a46846f0e1b95a0b2718ed707348b6bc1713b1 -size 107508 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_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.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index eb23dcec96..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b8de2f28cf9918a7eddcc1311c34ba0376242a4708724b57689df000b7480524 -size 8197 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_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.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index e2e4a33e67..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:687133e4729ea42382ac0b76af6ceaf8b20cbc65923412fb97b077d2475c70f7 -size 7514 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_3,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.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 2746320bc2..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8e1ca8a115c3e46c7f24fe41dc6c9448ffecc65a05c6be1945b84f35d60a099 -size 103642 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 385103eb68..6befe0b000 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df97ab1093fd2c257f98b16fefeb28cc4a31747a7a9bda0725fa826a26c59906 -size 14299 +oid sha256:3d885a482d61257aac3ea43fdd8440e617b45a094b1d213f1e00c7d4d684a83f +size 14468 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 982c03bc9a..9a35a8f08a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b3649fa2543fd138246ac3281f491f2a0a1c9f38c977e57a840cd560038af2f -size 13782 +oid sha256:e0ed25018ea703d7b72a68dfd55b7bb0023c1526e56b633ddf472fee261547a1 +size 13836 From 78e207918b93569867a22710708b7b001e48483e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:54:12 +0100 Subject: [PATCH 27/47] Fix name of the tests class --- .../selectusers/impl/DefaultSelectUsersPresenterTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index a00c7bd775..677bb68b12 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) -class DefaultSelectSingleUserPresenterTests { +class DefaultSelectUsersPresenterTests { @Test fun `present - initial state for single selection`() = runTest { From 09f95f1e98ddd89b55201891c9f1424d996bbd8b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:57:11 +0100 Subject: [PATCH 28/47] Fixing CreateRoomRootPresenterTests --- .../createroom/impl/root/CreateRoomRootPresenterTests.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 6a852f4599..d8e171892d 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,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.selectusers.impl.DefaultSelectUsersPresenter +import io.element.android.features.selectusers.impl.DefaultSelectSingleUserPresenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,7 +33,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - initial state`() = runTest { - val selectUsersPresenter = DefaultSelectUsersPresenter() + val selectUsersPresenter = DefaultSelectSingleUserPresenter() val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -45,7 +45,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger action buttons`() = runTest { - val selectUsersPresenter = DefaultSelectUsersPresenter() + val selectUsersPresenter = DefaultSelectSingleUserPresenter() val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -57,7 +57,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger start DM action`() = runTest { - val selectUsersPresenter = DefaultSelectUsersPresenter() + val selectUsersPresenter = DefaultSelectSingleUserPresenter() val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() From 77cbe0e67b8010dbac1b8f32ebbfb1f17cfb015b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:01:01 +0100 Subject: [PATCH 29/47] Adding unit tests for AddPeoplePresenter --- .../impl/addpeople/AddPeoplePresenter.kt | 1 - .../impl/addpeople/AddPeoplePresenterTests.kt | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt 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 23eb9adbec..6fab3f6ae8 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 @@ -23,7 +23,6 @@ import io.element.android.libraries.architecture.Presenter import javax.inject.Inject import javax.inject.Named -// TODO add unit tests class AddPeoplePresenter @Inject constructor( @Named(MULTI_SELECTION_USERS_VARIANT) private val selectUsersPresenter: SelectUsersPresenter, 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 new file mode 100644 index 0000000000..a03893f845 --- /dev/null +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -0,0 +1,43 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.createroom.impl.addpeople + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.selectusers.impl.DefaultSelectMultipleUsersPresenter +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class AddPeoplePresenterTests { + + @Test + fun `present - initial state`() = runTest { + val selectUsersPresenter = DefaultSelectMultipleUsersPresenter() + val presenter = AddPeoplePresenter(selectUsersPresenter) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState) + } + } +} From ae5b095711e1e4fb91f4358995ce9da7b9e23b67 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 10:01:44 +0100 Subject: [PATCH 30/47] Fixing unit tests of DefaultSelectUsersPresenter --- features/selectusers/impl/build.gradle.kts | 2 ++ .../selectusers/impl/DefaultSelectUsersPresenterTests.kt | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/features/selectusers/impl/build.gradle.kts b/features/selectusers/impl/build.gradle.kts index e74996cc5c..baac7d2d2f 100644 --- a/features/selectusers/impl/build.gradle.kts +++ b/features/selectusers/impl/build.gradle.kts @@ -46,9 +46,11 @@ dependencies { testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) + testImplementation(libs.coroutines.core) testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) androidTestImplementation(libs.test.junitext) diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index 677bb68b12..4935e9d330 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -16,6 +16,7 @@ package io.element.android.features.selectusers.impl +import androidx.compose.foundation.lazy.LazyListState import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test @@ -24,6 +25,8 @@ import io.element.android.features.selectusers.api.SelectUsersEvents 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 +import io.mockk.coJustRun +import io.mockk.mockkConstructor import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -89,6 +92,9 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - select a user`() = runTest { + mockkConstructor(LazyListState::class) + coJustRun { anyConstructed().scrollToItem(index = any()) } + val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) moleculeFlow(RecompositionClock.Immediate) { presenter.present() From 9fc9c13292116c0b0df25996f607afc0a0f446a6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 10:29:16 +0100 Subject: [PATCH 31/47] Adding missing screenshots for tests --- ...up_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...up_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...up_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...p_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...p_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...p_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...ckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...kableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...kableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ 10 files changed, 30 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,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.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_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.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,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.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,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.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_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.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png 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_ChangeServerViewDarkPreview_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.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..525854695a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55d774d5e61f8832162859ec9d22299efa5b0728cfc3308b13fb11df31c5130e +size 13790 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_ChangeServerViewDarkPreview_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_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ad94119f4c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:489aa166bf6d0f283da497b2752e457f81620959d404b5b9c3d96c7e18e8b953 +size 28100 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_ChangeServerViewDarkPreview_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_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..85c608618b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a8969c9a82f41bbdbdfb013412d039f3be4fcf450a0dd55f3217c64d51c8489 +size 23495 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_ChangeServerViewLightPreview_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.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8ea3902e3a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ed229820d7cbd6b550e98116ec3bf688c52957e2a57ab296bfc5abd212345b1 +size 13784 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_ChangeServerViewLightPreview_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_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2074516963 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5f29443193cd21eeb721a4c86f9063466439dd5ad4830708e6fa587f95e5abd +size 27456 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_ChangeServerViewLightPreview_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_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2139d8a6c5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa79c54d2431dc0c8399a2bcb8e03d73749e9d54f505ac893e2376fded0e7dfe +size 22599 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..02c9c42691 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1ec43b9fa3fd3906bc1d55145320a91a08f9222928ac55f7c264a7b5fa27ad8 +size 24303 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3f174ff793 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59478d13aa6150c097167e3b2d984f5f82d46a74326b7e69c74b9785cae9bd56 +size 23382 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1e8d75d01a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5c6225b33876b3a3878f0bfc230c081e798f6e0aae53286b93eb1ac76c17b35 +size 23116 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_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.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ca77c5fff --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d13731b708adb16d1524d66a13daea13071cfd5ba646d21ef80fd682c28f6a4 +size 22450 From 244f080e990a5fc39181e1d43f4ba4fbcc8e4e0c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:53:04 +0100 Subject: [PATCH 32/47] Delete manifest file --- .../impl/src/main/AndroidManifest.xml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 features/selectusers/impl/src/main/AndroidManifest.xml diff --git a/features/selectusers/impl/src/main/AndroidManifest.xml b/features/selectusers/impl/src/main/AndroidManifest.xml deleted file mode 100644 index e9c0841b6b..0000000000 --- a/features/selectusers/impl/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - From dea453b7c3103bb184641f8deafa37f5e25c7781 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:56:59 +0100 Subject: [PATCH 33/47] Fix naming for the new previews --- .../features/createroom/impl/addpeople/AddPeopleView.kt | 4 ++-- ...p_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 --- ...p_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 --- ...p_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 --- ..._ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 --- ..._ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 --- ..._ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 --- 7 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png 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 842256d9c3..ba2abb498d 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 ChangeServerViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = +internal fun AddPeopleViewLightPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun ChangeServerViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = +internal fun AddPeopleViewDarkPreview(@PreviewParameter(AddPeopleStateProvider::class) state: AddPeopleState) = ElementPreviewDark { ContentToPreview(state) } @Composable 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_ChangeServerViewDarkPreview_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.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 525854695a..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55d774d5e61f8832162859ec9d22299efa5b0728cfc3308b13fb11df31c5130e -size 13790 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_ChangeServerViewDarkPreview_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_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index ad94119f4c..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:489aa166bf6d0f283da497b2752e457f81620959d404b5b9c3d96c7e18e8b953 -size 28100 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_ChangeServerViewDarkPreview_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_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 85c608618b..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0a8969c9a82f41bbdbdfb013412d039f3be4fcf450a0dd55f3217c64d51c8489 -size 23495 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_ChangeServerViewLightPreview_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.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 8ea3902e3a..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ed229820d7cbd6b550e98116ec3bf688c52957e2a57ab296bfc5abd212345b1 -size 13784 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_ChangeServerViewLightPreview_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_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 2074516963..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5f29443193cd21eeb721a4c86f9063466439dd5ad4830708e6fa587f95e5abd -size 27456 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_ChangeServerViewLightPreview_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_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 2139d8a6c5..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fa79c54d2431dc0c8399a2bcb8e03d73749e9d54f505ac893e2376fded0e7dfe -size 22599 From ceef65316e61caaa78858db46537b760ea95156c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 17:19:56 +0100 Subject: [PATCH 34/47] Using nullable onClick to avoid passing a new parameter --- .../libraries/matrix/ui/components/CheckableMatrixUserRow.kt | 1 - .../android/libraries/matrix/ui/components/MatrixUserRow.kt | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt index 67969f8e2e..f75f998d8f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt @@ -53,7 +53,6 @@ fun CheckableMatrixUserRow( modifier = Modifier.weight(1f), matrixUser = matrixUser, avatarSize = avatarSize, - isClickEnabled = false, ) Checkbox( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index 768c803850..b92393af2f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -45,12 +45,11 @@ fun MatrixUserRow( matrixUser: MatrixUser, modifier: Modifier = Modifier, avatarSize: AvatarSize = matrixUser.avatarData.size, - isClickEnabled: Boolean = true, - onClick: () -> Unit = {}, + onClick: (() -> Unit)? = null, ) { Row( modifier = modifier - .let { if (isClickEnabled) it.clickable(onClick = onClick) else it } + .let { if (onClick != null) it.clickable(onClick = onClick) else it } .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp) .height(IntrinsicSize.Min), verticalAlignment = Alignment.CenterVertically From a1fe191b6414beafa6b20b2cc5c2e2e81800e9b3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Fri, 17 Mar 2023 17:21:43 +0100 Subject: [PATCH 35/47] Reuse fillMaxWidth on MatrixUserRow --- .../android/libraries/matrix/ui/components/MatrixUserRow.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index b92393af2f..0dc6ae6b02 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -50,6 +51,7 @@ fun MatrixUserRow( Row( modifier = modifier .let { if (onClick != null) it.clickable(onClick = onClick) else it } + .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp) .height(IntrinsicSize.Min), verticalAlignment = Alignment.CenterVertically From d08a1314293cd38032bc5e5201852f0c83b865ca Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 22 Mar 2023 11:31:38 +0100 Subject: [PATCH 36/47] Remove named injection and provide presenter through a factory --- .../impl/addpeople/AddPeoplePresenter.kt | 10 +++--- .../impl/root/CreateRoomRootPresenter.kt | 12 ++++--- .../impl/addpeople/AddPeoplePresenterTests.kt | 16 +++++++-- .../impl/root/CreateRoomRootPresenterTests.kt | 20 +++++++---- .../api/SelectMultipleUsersPresenter.kt | 21 ------------ .../selectusers/api/SelectUsersPresenter.kt | 8 +++-- ...esenter.kt => SelectUsersPresenterArgs.kt} | 6 ++-- .../DefaultSelectMultipleUsersPresenter.kt | 33 ------------------- .../impl/DefaultSelectSingleUserPresenter.kt | 32 ------------------ .../impl/DefaultSelectUsersPresenter.kt | 18 ++++++++-- .../impl/DefaultSelectUsersPresenterTests.kt | 9 ++--- 11 files changed, 68 insertions(+), 117 deletions(-) delete mode 100644 features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt rename features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/{SelectSingleUserPresenter.kt => SelectUsersPresenterArgs.kt} (83%) delete mode 100644 features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt delete mode 100644 features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt 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 6fab3f6ae8..d16ad164fc 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,17 +17,19 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable -import io.element.android.features.selectusers.api.MULTI_SELECTION_USERS_VARIANT import io.element.android.features.selectusers.api.SelectUsersPresenter +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs import io.element.android.libraries.architecture.Presenter import javax.inject.Inject -import javax.inject.Named class AddPeoplePresenter @Inject constructor( - @Named(MULTI_SELECTION_USERS_VARIANT) - private val selectUsersPresenter: SelectUsersPresenter, + private val selectUsersPresenterFactory: SelectUsersPresenter.Factory, ) : Presenter { + private val selectUsersPresenter by lazy { + selectUsersPresenterFactory.create(SelectUsersPresenterArgs(isMultiSelectionEnabled = true)) + } + @Composable override fun present(): AddPeopleState { val selectUsersState = selectUsersPresenter.present() 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 b7bca3a564..b30483bd61 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 @@ -17,22 +17,24 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable -import io.element.android.features.selectusers.api.SINGLE_SELECTION_USERS_VARIANT import io.element.android.features.selectusers.api.SelectUsersPresenter +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.ui.model.MatrixUser import timber.log.Timber import javax.inject.Inject -import javax.inject.Named class CreateRoomRootPresenter @Inject constructor( - @Named(SINGLE_SELECTION_USERS_VARIANT) - private val selectUsersPresenter: SelectUsersPresenter, + private val presenterFactory: SelectUsersPresenter.Factory, ) : Presenter { + private val presenter by lazy { + presenterFactory.create(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) + } + @Composable override fun present(): CreateRoomRootState { - val selectUsersState = selectUsersPresenter.present() + val selectUsersState = presenter.present() fun handleEvents(event: CreateRoomRootEvents) { when (event) { 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 a03893f845..d9f40c1dbf 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,17 +22,27 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.selectusers.impl.DefaultSelectMultipleUsersPresenter +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs +import io.element.android.features.selectusers.impl.DefaultSelectUsersPresenter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test class AddPeoplePresenterTests { + private lateinit var presenter: AddPeoplePresenter + + @Before + fun setup() { + val selectUsersFactory = object : DefaultSelectUsersPresenter.DefaultSelectUsersFactory { + override fun create(args: SelectUsersPresenterArgs) = DefaultSelectUsersPresenter(args) + } + presenter = AddPeoplePresenter(selectUsersFactory) + } + @Test fun `present - initial state`() = runTest { - val selectUsersPresenter = DefaultSelectMultipleUsersPresenter() - val presenter = AddPeoplePresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.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 d8e171892d..5dc9ec00e9 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,19 +22,29 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.selectusers.impl.DefaultSelectSingleUserPresenter +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs +import io.element.android.features.selectusers.impl.DefaultSelectUsersPresenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test class CreateRoomRootPresenterTests { + private lateinit var presenter: CreateRoomRootPresenter + + @Before + fun setup() { + val selectUsersPresenter = object : DefaultSelectUsersPresenter.DefaultSelectUsersFactory { + override fun create(args: SelectUsersPresenterArgs) = DefaultSelectUsersPresenter(args) + } + presenter = CreateRoomRootPresenter(selectUsersPresenter) + } + @Test fun `present - initial state`() = runTest { - val selectUsersPresenter = DefaultSelectSingleUserPresenter() - val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -45,8 +55,6 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger action buttons`() = runTest { - val selectUsersPresenter = DefaultSelectSingleUserPresenter() - val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -57,8 +65,6 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger start DM action`() = runTest { - val selectUsersPresenter = DefaultSelectSingleUserPresenter() - val presenter = CreateRoomRootPresenter(selectUsersPresenter) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt deleted file mode 100644 index 08228e048d..0000000000 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectMultipleUsersPresenter.kt +++ /dev/null @@ -1,21 +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.selectusers.api - -import io.element.android.libraries.architecture.Presenter - -interface SelectMultipleUsersPresenter : Presenter diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt index 913815c698..be85455d09 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenter.kt @@ -18,7 +18,9 @@ package io.element.android.features.selectusers.api import io.element.android.libraries.architecture.Presenter -const val SINGLE_SELECTION_USERS_VARIANT = "single_selection_users" -const val MULTI_SELECTION_USERS_VARIANT = "multi_selection_users" +interface SelectUsersPresenter : Presenter { -interface SelectUsersPresenter : Presenter + interface Factory { + fun create(args: SelectUsersPresenterArgs): SelectUsersPresenter + } +} diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt similarity index 83% rename from features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt rename to features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt index a0755e4375..35db78eaac 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectSingleUserPresenter.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt @@ -16,6 +16,6 @@ package io.element.android.features.selectusers.api -import io.element.android.libraries.architecture.Presenter - -interface SelectSingleUserPresenter : Presenter +data class SelectUsersPresenterArgs( + val isMultiSelectionEnabled: Boolean, +) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt deleted file mode 100644 index f2d6be0124..0000000000 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectMultipleUsersPresenter.kt +++ /dev/null @@ -1,33 +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.selectusers.impl - -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.selectusers.api.MULTI_SELECTION_USERS_VARIANT -import io.element.android.features.selectusers.api.SelectUsersPresenter -import io.element.android.libraries.di.SessionScope -import javax.inject.Inject -import javax.inject.Named - -@ContributesBinding( - scope = SessionScope::class, - boundType = SelectUsersPresenter::class -) -@Named(MULTI_SELECTION_USERS_VARIANT) -class DefaultSelectMultipleUsersPresenter @Inject constructor() : - SelectUsersPresenter by DefaultSelectUsersPresenter(isMultiSelectionEnabled = true) - diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt deleted file mode 100644 index ea538e0e39..0000000000 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectSingleUserPresenter.kt +++ /dev/null @@ -1,32 +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.selectusers.impl - -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.selectusers.api.SINGLE_SELECTION_USERS_VARIANT -import io.element.android.features.selectusers.api.SelectUsersPresenter -import io.element.android.libraries.di.SessionScope -import javax.inject.Inject -import javax.inject.Named - -@ContributesBinding( - scope = SessionScope::class, - boundType = SelectUsersPresenter::class -) -@Named(SINGLE_SELECTION_USERS_VARIANT) -class DefaultSelectSingleUserPresenter @Inject constructor() : - SelectUsersPresenter by DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 5e194adcc8..e1522e83d2 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -27,9 +27,15 @@ 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 +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import io.element.android.features.selectusers.api.SelectUsersEvents import io.element.android.features.selectusers.api.SelectUsersPresenter +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs import io.element.android.features.selectusers.api.SelectUsersState +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 import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -42,7 +48,15 @@ import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter { +class DefaultSelectUsersPresenter @AssistedInject constructor( + @Assisted val args: SelectUsersPresenterArgs, +) : SelectUsersPresenter { + + @AssistedFactory + @ContributesBinding(SessionScope::class) + interface DefaultSelectUsersFactory : SelectUsersPresenter.Factory { + override fun create(args: SelectUsersPresenterArgs): DefaultSelectUsersPresenter + } @Composable override fun present(): SelectUsersState { @@ -88,7 +102,7 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) selectedUsers = selectedUsers.value.reversed().toImmutableSet(), selectedUsersListState = selectedUsersListState, isSearchActive = isSearchActive, - isMultiSelectionEnabled = isMultiSelectionEnabled, + isMultiSelectionEnabled = args.isMultiSelectionEnabled, eventSink = ::handleEvents, ) } diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index 4935e9d330..db4e74a5f1 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.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.selectusers.api.SelectUsersEvents +import io.element.android.features.selectusers.api.SelectUsersPresenterArgs 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 @@ -36,7 +37,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - initial state for single selection`() = runTest { - val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -51,7 +52,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - initial state for multiple selection`() = runTest { - val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = true) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = true)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -66,7 +67,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - update search query`() = runTest { - val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -95,7 +96,7 @@ class DefaultSelectUsersPresenterTests { mockkConstructor(LazyListState::class) coJustRun { anyConstructed().scrollToItem(index = any()) } - val presenter = DefaultSelectUsersPresenter(isMultiSelectionEnabled = false) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { From 9153d8ced345c40ed09290929dd3b712d94da2f3 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 22 Mar 2023 11:35:07 +0100 Subject: [PATCH 37/47] Introduce SelectionMode enum --- .../createroom/impl/addpeople/AddPeoplePresenter.kt | 3 ++- .../createroom/impl/addpeople/AddPeopleStateProvider.kt | 5 +++-- .../createroom/impl/root/CreateRoomRootPresenter.kt | 3 ++- .../features/selectusers/api/SelectUsersPresenterArgs.kt | 7 ++++++- .../android/features/selectusers/api/SelectUsersState.kt | 6 ++++-- .../features/selectusers/api/SelectUsersStateProvider.kt | 8 ++++---- .../selectusers/impl/DefaultSelectUsersPresenter.kt | 2 +- .../selectusers/impl/DefaultSelectUsersPresenterTests.kt | 9 +++++---- 8 files changed, 27 insertions(+), 16 deletions(-) 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 d16ad164fc..51b2928862 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 @@ -19,6 +19,7 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.features.selectusers.api.SelectUsersPresenterArgs +import io.element.android.features.selectusers.api.SelectionMode import io.element.android.libraries.architecture.Presenter import javax.inject.Inject @@ -27,7 +28,7 @@ class AddPeoplePresenter @Inject constructor( ) : Presenter { private val selectUsersPresenter by lazy { - selectUsersPresenterFactory.create(SelectUsersPresenterArgs(isMultiSelectionEnabled = true)) + selectUsersPresenterFactory.create(SelectUsersPresenterArgs(SelectionMode.Multiple)) } @Composable 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 37428071d7..a1c0e6bdf2 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 @@ -17,6 +17,7 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.selectusers.api.SelectionMode import io.element.android.features.selectusers.api.aSelectUsersState import io.element.android.features.selectusers.api.aSetOfSelectedUsers @@ -27,14 +28,14 @@ open class AddPeopleStateProvider : PreviewParameterProvider { aAddPeopleState().copy( selectUsersState = aSelectUsersState().copy( selectedUsers = aSetOfSelectedUsers(), - isMultiSelectionEnabled = true, + selectionMode = SelectionMode.Multiple, ) ), aAddPeopleState().copy( selectUsersState = aSelectUsersState().copy( selectedUsers = aSetOfSelectedUsers(), isSearchActive = true, - isMultiSelectionEnabled = true, + selectionMode = SelectionMode.Multiple, ) ) ) 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 b30483bd61..2f3f3ded4a 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 @@ -19,6 +19,7 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable import io.element.android.features.selectusers.api.SelectUsersPresenter import io.element.android.features.selectusers.api.SelectUsersPresenterArgs +import io.element.android.features.selectusers.api.SelectionMode import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.ui.model.MatrixUser import timber.log.Timber @@ -29,7 +30,7 @@ class CreateRoomRootPresenter @Inject constructor( ) : Presenter { private val presenter by lazy { - presenterFactory.create(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) + presenterFactory.create(SelectUsersPresenterArgs(SelectionMode.Single)) } @Composable diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt index 35db78eaac..543e73b77e 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersPresenterArgs.kt @@ -17,5 +17,10 @@ package io.element.android.features.selectusers.api data class SelectUsersPresenterArgs( - val isMultiSelectionEnabled: Boolean, + val selectionMode: SelectionMode, ) + +enum class SelectionMode { + Single, + Multiple, +} diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 86437d2872..a858703350 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -27,6 +27,8 @@ data class SelectUsersState( val selectedUsers: ImmutableSet, val selectedUsersListState: LazyListState, val isSearchActive: Boolean, - val isMultiSelectionEnabled: Boolean, + val selectionMode: SelectionMode, val eventSink: (SelectUsersEvents) -> Unit, -) +) { + val isMultiSelectionEnabled = selectionMode == SelectionMode.Multiple +} diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index e85b68e986..790bd010c0 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -30,11 +30,11 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState().copy( isSearchActive = false, selectedUsers = aSetOfSelectedUsers(), - isMultiSelectionEnabled = true + selectionMode = SelectionMode.Multiple, ), aSelectUsersState().copy(isSearchActive = true), aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone"), - aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone", isMultiSelectionEnabled = true), + aSelectUsersState().copy(isSearchActive = true, searchQuery = "someone", selectionMode = SelectionMode.Multiple), aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", @@ -44,7 +44,7 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", - isMultiSelectionEnabled = true, + selectionMode = SelectionMode.Multiple, selectedUsers = aSetOfSelectedUsers(), searchResults = aListOfResults(), ) @@ -60,7 +60,7 @@ fun aSelectUsersState() = SelectUsersState( firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0, ), - isMultiSelectionEnabled = false, + selectionMode = SelectionMode.Single, eventSink = {} ) diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index e1522e83d2..2176317aba 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -102,7 +102,7 @@ class DefaultSelectUsersPresenter @AssistedInject constructor( selectedUsers = selectedUsers.value.reversed().toImmutableSet(), selectedUsersListState = selectedUsersListState, isSearchActive = isSearchActive, - isMultiSelectionEnabled = args.isMultiSelectionEnabled, + selectionMode = args.selectionMode, eventSink = ::handleEvents, ) } diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index db4e74a5f1..0df0ec88ec 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -23,6 +23,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.selectusers.api.SelectUsersEvents import io.element.android.features.selectusers.api.SelectUsersPresenterArgs +import io.element.android.features.selectusers.api.SelectionMode 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 +38,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - initial state for single selection`() = runTest { - val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(SelectionMode.Single)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -52,7 +53,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - initial state for multiple selection`() = runTest { - val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = true)) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(SelectionMode.Multiple)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -67,7 +68,7 @@ class DefaultSelectUsersPresenterTests { @Test fun `present - update search query`() = runTest { - val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(SelectionMode.Single)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -96,7 +97,7 @@ class DefaultSelectUsersPresenterTests { mockkConstructor(LazyListState::class) coJustRun { anyConstructed().scrollToItem(index = any()) } - val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(isMultiSelectionEnabled = false)) + val presenter = DefaultSelectUsersPresenter(SelectUsersPresenterArgs(SelectionMode.Single)) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { From e4e58178e57f7ddf515c294cc4265dd47f5b34e3 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 23 Mar 2023 10:16:11 +0100 Subject: [PATCH 38/47] remove unused imports --- .../features/createroom/impl/root/CreateRoomRootState.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt index b7be3f44fb..a57d6aaaf6 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootState.kt @@ -17,8 +17,6 @@ package io.element.android.features.createroom.impl.root import io.element.android.features.selectusers.api.SelectUsersState -import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList data class CreateRoomRootState( val selectUsersState: SelectUsersState, From aec1ce224402563d3b2b0661b84f284bf7c0f2c0 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 23 Mar 2023 10:19:04 +0100 Subject: [PATCH 39/47] Update screenshots --- ...roup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...roup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...roup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...oup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...oup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...oup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...roup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- 8 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 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_0,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.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_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.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,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.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,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.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_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.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png 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_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.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..525854695a --- /dev/null +++ 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_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55d774d5e61f8832162859ec9d22299efa5b0728cfc3308b13fb11df31c5130e +size 13790 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 new file mode 100644 index 0000000000..ad94119f4c --- /dev/null +++ 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 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:489aa166bf6d0f283da497b2752e457f81620959d404b5b9c3d96c7e18e8b953 +size 28100 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 new file mode 100644 index 0000000000..85c608618b --- /dev/null +++ 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 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a8969c9a82f41bbdbdfb013412d039f3be4fcf450a0dd55f3217c64d51c8489 +size 23495 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_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.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8ea3902e3a --- /dev/null +++ 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_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ed229820d7cbd6b550e98116ec3bf688c52957e2a57ab296bfc5abd212345b1 +size 13784 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 new file mode 100644 index 0000000000..2074516963 --- /dev/null +++ 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 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5f29443193cd21eeb721a4c86f9063466439dd5ad4830708e6fa587f95e5abd +size 27456 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 new file mode 100644 index 0000000000..2139d8a6c5 --- /dev/null +++ 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 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa79c54d2431dc0c8399a2bcb8e03d73749e9d54f505ac893e2376fded0e7dfe +size 22599 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 6befe0b000..385103eb68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d885a482d61257aac3ea43fdd8440e617b45a094b1d213f1e00c7d4d684a83f -size 14468 +oid sha256:df97ab1093fd2c257f98b16fefeb28cc4a31747a7a9bda0725fa826a26c59906 +size 14299 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 9a35a8f08a..982c03bc9a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_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.libraries.matrix.ui.components_null_DefaultGroup_MatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0ed25018ea703d7b72a68dfd55b7bb0023c1526e56b633ddf472fee261547a1 -size 13836 +oid sha256:3b3649fa2543fd138246ac3281f491f2a0a1c9f38c977e57a840cd560038af2f +size 13782 From 6f299dd7f9f37508da17c23058102aa0e89d72c5 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 23 Mar 2023 10:28:57 +0100 Subject: [PATCH 40/47] remove useless eventSink variable --- .../android/features/selectusers/api/SelectUsersView.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 4e07ebd0ca..b4e97b5bd1 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -71,8 +71,6 @@ fun SelectUsersView( onUserSelected: (MatrixUser) -> Unit = {}, onUserDeselected: (MatrixUser) -> Unit = {}, ) { - val eventSink = state.eventSink - Column( modifier = modifier, ) { @@ -84,14 +82,14 @@ fun SelectUsersView( selectedUsersListState = state.selectedUsersListState, active = state.isSearchActive, isMultiSelectionEnabled = state.isMultiSelectionEnabled, - onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, + onActiveChanged = { state.eventSink(SelectUsersEvents.OnSearchActiveChanged(it)) }, onTextChanged = { state.eventSink(SelectUsersEvents.UpdateSearchQuery(it)) }, onUserSelected = { state.eventSink(SelectUsersEvents.AddToSelection(it)) onUserSelected(it) }, onUserDeselected = { - eventSink(SelectUsersEvents.RemoveFromSelection(it)) + state.eventSink(SelectUsersEvents.RemoveFromSelection(it)) onUserDeselected(it) }, ) @@ -102,7 +100,7 @@ fun SelectUsersView( modifier = Modifier.padding(16.dp), selectedUsers = state.selectedUsers, onUserRemoved = { - eventSink(SelectUsersEvents.RemoveFromSelection(it)) + state.eventSink(SelectUsersEvents.RemoveFromSelection(it)) onUserDeselected(it) }, ) From 4f6acc12a29a6bdc0d698cf30a9f00589692120c Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 23 Mar 2023 11:10:24 +0100 Subject: [PATCH 41/47] use list instead of set --- .../impl/addpeople/AddPeopleStateProvider.kt | 6 ++-- .../selectusers/api/SelectUsersState.kt | 3 +- .../api/SelectUsersStateProvider.kt | 11 ++++---- .../selectusers/api/SelectUsersView.kt | 5 ++-- .../impl/DefaultSelectUsersPresenter.kt | 15 +++++----- .../impl/DefaultSelectUsersPresenterTests.kt | 28 +++++++++---------- 6 files changed, 32 insertions(+), 36 deletions(-) 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 a1c0e6bdf2..6f1679e252 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,8 +18,8 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.selectusers.api.SelectionMode +import io.element.android.features.selectusers.api.aListOfSelectedUsers import io.element.android.features.selectusers.api.aSelectUsersState -import io.element.android.features.selectusers.api.aSetOfSelectedUsers open class AddPeopleStateProvider : PreviewParameterProvider { override val values: Sequence @@ -27,13 +27,13 @@ open class AddPeopleStateProvider : PreviewParameterProvider { aAddPeopleState(), aAddPeopleState().copy( selectUsersState = aSelectUsersState().copy( - selectedUsers = aSetOfSelectedUsers(), + selectedUsers = aListOfSelectedUsers(), selectionMode = SelectionMode.Multiple, ) ), aAddPeopleState().copy( selectUsersState = aSelectUsersState().copy( - selectedUsers = aSetOfSelectedUsers(), + selectedUsers = aListOfSelectedUsers(), isSearchActive = true, selectionMode = SelectionMode.Multiple, ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index a858703350..2a1a2c48e3 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -19,12 +19,11 @@ package io.element.android.features.selectusers.api import androidx.compose.foundation.lazy.LazyListState import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableSet data class SelectUsersState( val searchQuery: String, val searchResults: ImmutableList, - val selectedUsers: ImmutableSet, + val selectedUsers: ImmutableList, val selectedUsersListState: LazyListState, val isSearchActive: Boolean, val selectionMode: SelectionMode, diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 790bd010c0..93209a632c 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -21,7 +21,6 @@ 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 kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.persistentSetOf open class SelectUsersStateProvider : PreviewParameterProvider { override val values: Sequence @@ -29,7 +28,7 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState(), aSelectUsersState().copy( isSearchActive = false, - selectedUsers = aSetOfSelectedUsers(), + selectedUsers = aListOfSelectedUsers(), selectionMode = SelectionMode.Multiple, ), aSelectUsersState().copy(isSearchActive = true), @@ -38,14 +37,14 @@ open class SelectUsersStateProvider : PreviewParameterProvider aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", - selectedUsers = aSetOfSelectedUsers(), + selectedUsers = aListOfSelectedUsers(), searchResults = aListOfResults(), ), aSelectUsersState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", selectionMode = SelectionMode.Multiple, - selectedUsers = aSetOfSelectedUsers(), + selectedUsers = aListOfSelectedUsers(), searchResults = aListOfResults(), ) ) @@ -55,7 +54,7 @@ fun aSelectUsersState() = SelectUsersState( isSearchActive = false, searchQuery = "", searchResults = persistentListOf(), - selectedUsers = persistentSetOf(), + selectedUsers = persistentListOf(), selectedUsersListState = LazyListState( firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0, @@ -64,7 +63,7 @@ fun aSelectUsersState() = SelectUsersState( eventSink = {} ) -fun aSetOfSelectedUsers() = persistentSetOf( +fun aListOfSelectedUsers() = persistentListOf( MatrixUser(id = UserId("@someone:matrix.org")), MatrixUser(id = UserId("@other:matrix.org"), username = "other"), ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index b4e97b5bd1..6703aacac6 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -61,7 +61,6 @@ 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 kotlinx.collections.immutable.ImmutableSet import io.element.android.libraries.ui.strings.R as StringR @Composable @@ -113,7 +112,7 @@ fun SelectUsersView( fun SearchUserBar( query: String, results: ImmutableList, - selectedUsers: ImmutableSet, + selectedUsers: ImmutableList, selectedUsersListState: LazyListState, active: Boolean, isMultiSelectionEnabled: Boolean, @@ -243,7 +242,7 @@ fun SearchSingleUserResultItem( @Composable fun SelectedUsersList( listState: LazyListState, - selectedUsers: ImmutableSet, + selectedUsers: ImmutableList, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit = {}, ) { diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index 2176317aba..e1135cd1a2 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -40,11 +40,8 @@ import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -62,8 +59,8 @@ class DefaultSelectUsersPresenter @AssistedInject constructor( override fun present(): SelectUsersState { val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers: MutableState> = remember { - mutableStateOf(persistentSetOf()) + val selectedUsers: MutableState> = remember { + mutableStateOf(persistentListOf()) } val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } @@ -76,10 +73,12 @@ class DefaultSelectUsersPresenter @AssistedInject constructor( is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query is SelectUsersEvents.AddToSelection -> { - selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet() + if (event.matrixUser !in selectedUsers.value) { + selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + } localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) } - is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableSet() + is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() } } @@ -99,7 +98,7 @@ class DefaultSelectUsersPresenter @AssistedInject constructor( return SelectUsersState( searchQuery = searchQuery, searchResults = searchResults.value, - selectedUsers = selectedUsers.value.reversed().toImmutableSet(), + selectedUsers = selectedUsers.value.reversed().toImmutableList(), selectedUsersListState = selectedUsersListState, isSearchActive = isSearchActive, selectionMode = args.selectionMode, diff --git a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt index 0df0ec88ec..f5b0b43d0e 100644 --- a/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt +++ b/features/selectusers/impl/src/test/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenterTests.kt @@ -105,27 +105,27 @@ class DefaultSelectUsersPresenterTests { val userA = aMatrixUser("userA", "A") val userB = aMatrixUser("userB", "B") + val userABis = aMatrixUser("userA", "A") + val userC = aMatrixUser("userC", "C") + initialState.eventSink(SelectUsersEvents.AddToSelection(userA)) - val selectionAfterAddingA = awaitItem().selectedUsers - assertThat(selectionAfterAddingA).hasSize(1) - assertThat(selectionAfterAddingA).contains(userA) + assertThat(awaitItem().selectedUsers).containsExactly(userA) initialState.eventSink(SelectUsersEvents.AddToSelection(userB)) - val selectionAfterAddingB = awaitItem().selectedUsers - assertThat(selectionAfterAddingB).hasSize(2) - assertThat(selectionAfterAddingB).contains(userA) - assertThat(selectionAfterAddingB).contains(userB) // the last added user should be presented first - assertThat(selectionAfterAddingB.first()).isEqualTo(userB) + assertThat(awaitItem().selectedUsers).containsExactly(userB, userA) - initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userB)) - val selectionAfterRemovingB = awaitItem().selectedUsers - assertThat(selectionAfterRemovingB).hasSize(1) - assertThat(selectionAfterRemovingB).contains(userA) + initialState.eventSink(SelectUsersEvents.AddToSelection(userABis)) + initialState.eventSink(SelectUsersEvents.AddToSelection(userC)) + // duplicated users should be ignored + assertThat(awaitItem().selectedUsers).containsExactly(userC, userB, userA) + initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userB)) + assertThat(awaitItem().selectedUsers).containsExactly(userC, userA) initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userA)) - val selectionAfterRemovingA = awaitItem().selectedUsers - assertThat(selectionAfterRemovingA).isEmpty() + assertThat(awaitItem().selectedUsers).containsExactly(userC) + initialState.eventSink(SelectUsersEvents.RemoveFromSelection(userC)) + assertThat(awaitItem().selectedUsers).isEmpty() } } } From ac8a6f90fd65c1ee8d59605f211081631a72adbb Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 23 Mar 2023 11:46:56 +0100 Subject: [PATCH 42/47] Remove default onClick from MatrixUserRow --- .../android/features/selectusers/api/SelectUsersView.kt | 4 ++-- .../libraries/matrix/ui/components/CheckableMatrixUserRow.kt | 5 ++--- .../android/libraries/matrix/ui/components/MatrixUserRow.kt | 3 --- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 6703aacac6..60d7785b69 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -17,6 +17,7 @@ package io.element.android.features.selectusers.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 @@ -232,10 +233,9 @@ fun SearchSingleUserResultItem( onClick: () -> Unit = {}, ) { MatrixUserRow( - modifier = modifier, + modifier = modifier.clickable(onClick = onClick), matrixUser = matrixUser, avatarSize = AvatarSize.Custom(36.dp), - onClick = onClick, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt index f75f998d8f..e61f02ebf6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -44,9 +45,7 @@ fun CheckableMatrixUserRow( Row( modifier = modifier .fillMaxWidth() - .clickable { - onCheckedChange(!checked) - }, + .clickable(role = Role.Checkbox) { onCheckedChange(!checked) }, verticalAlignment = Alignment.CenterVertically, ) { MatrixUserRow( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index 0dc6ae6b02..e4d2386c1c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.matrix.ui.components -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row @@ -46,11 +45,9 @@ fun MatrixUserRow( matrixUser: MatrixUser, modifier: Modifier = Modifier, avatarSize: AvatarSize = matrixUser.avatarData.size, - onClick: (() -> Unit)? = null, ) { Row( modifier = modifier - .let { if (onClick != null) it.clickable(onClick = onClick) else it } .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp) .height(IntrinsicSize.Min), From 09c275d883753c45142342049bb56c638344ecf1 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 23 Mar 2023 14:24:25 +0100 Subject: [PATCH 43/47] [Verify Session] Add missing 'start verification' screen. (#241) * Add missing 'start verification' screen. * Fix retries sometimes not working because of `SessionVerificationController.setDelegate`. * Make the negative button invisible when not needed. --- .../impl/VerifySelfSessionPresenter.kt | 5 +++- .../impl/VerifySelfSessionState.kt | 1 + .../impl/VerifySelfSessionStateMachine.kt | 2 +- .../impl/VerifySelfSessionStateProvider.kt | 3 +++ .../impl/VerifySelfSessionView.kt | 26 +++++++++++-------- .../impl/VerifySelfSessionPresenterTests.kt | 8 ++++-- .../RustSessionVerificationService.kt | 3 +-- .../src/main/res/values/strings_eax.xml | 1 + ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ 10 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt index 5f43372e55..aa1fe59c32 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt @@ -85,11 +85,14 @@ class VerifySelfSessionPresenter @Inject constructor( StateMachineState.RequestingVerification, StateMachineState.StartingSasVerification, StateMachineState.SasVerificationStarted, - StateMachineState.VerificationRequestAccepted, StateMachineState.Canceling -> { VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse } + StateMachineState.VerificationRequestAccepted -> { + VerifySelfSessionState.VerificationStep.Ready + } + StateMachineState.Canceled -> { VerifySelfSessionState.VerificationStep.Canceled } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt index 6f24e238cc..752cf942c1 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt @@ -32,6 +32,7 @@ data class VerifySelfSessionState( object Initial : VerificationStep object Canceled : VerificationStep object AwaitingOtherDeviceResponse : VerificationStep + object Ready : VerificationStep data class Verifying(val emojiList: List, val state: Async) : VerificationStep object Completed : VerificationStep } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt index 466db2aa99..3c905334eb 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt @@ -81,6 +81,7 @@ class VerifySelfSessionStateMachine( // Observe the verification service state, translate it to state machine input events sessionVerificationService.verificationFlowState.onEach { verificationAttemptState -> when (verificationAttemptState) { + VerificationFlowState.Initial -> stateMachine.restart() VerificationFlowState.AcceptedVerificationRequest -> { stateMachine.process(Event.DidAcceptVerificationRequest) } @@ -102,7 +103,6 @@ class VerifySelfSessionStateMachine( VerificationFlowState.Failed -> { stateMachine.process(Event.DidFail) } - else -> Unit } }.launchIn(coroutineScope) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt index 01b0e6bea6..54a59dee01 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt @@ -36,6 +36,9 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider R.drawable.ic_verification_devices FlowStep.Canceled -> R.drawable.ic_verification_warning FlowStep.AwaitingOtherDeviceResponse -> R.drawable.ic_verification_waiting - is FlowStep.Verifying, FlowStep.Completed -> R.drawable.ic_verification_emoji + FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> R.drawable.ic_verification_emoji } val titleTextId = when (verificationFlowStep) { FlowStep.Initial -> StringR.string.verification_title_initial FlowStep.Canceled -> StringR.string.verification_title_canceled FlowStep.AwaitingOtherDeviceResponse -> StringR.string.verification_title_waiting - is FlowStep.Verifying, FlowStep.Completed -> StringR.string.verification_title_verifying + FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> StringR.string.verification_title_verifying } val subtitleTextId = when (verificationFlowStep) { FlowStep.Initial -> StringR.string.verification_subtitle_initial FlowStep.Canceled -> StringR.string.verification_subtitle_canceled FlowStep.AwaitingOtherDeviceResponse -> StringR.string.verification_subtitle_waiting - is FlowStep.Verifying, FlowStep.Completed -> StringR.string.verification_subtitle_verifying + FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> StringR.string.verification_subtitle_verifying } Column(modifier) { Spacer(Modifier.height(68.dp)) @@ -167,7 +167,7 @@ internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { Column(modifier) { Spacer(Modifier.height(56.dp)) when (flowState) { - FlowStep.Initial, FlowStep.Canceled, FlowStep.Completed -> Unit + FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting() is FlowStep.Verifying -> ContentVerifying(flowState) } @@ -219,6 +219,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) StringR.string.verification_positive_button_verifying_start } } + FlowStep.Ready -> StringR.string.verification_positive_button_ready else -> null } val negativeButtonTitle = when (verificationViewState) { @@ -231,6 +232,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) val positiveButtonEvent = when (verificationViewState) { FlowStep.Initial -> VerifySelfSessionViewEvents.RequestVerification + FlowStep.Ready -> VerifySelfSessionViewEvents.StartSasVerification is FlowStep.Verifying -> if (!isVerifying) VerifySelfSessionViewEvents.ConfirmVerification else null FlowStep.Canceled -> VerifySelfSessionViewEvents.Restart else -> null @@ -264,13 +266,15 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) positiveButtonTitle?.let { Text(stringResource(it)) } } } - Spacer(modifier = Modifier.height(16.dp)) - TextButton( - modifier = Modifier.fillMaxWidth(), - onClick = negativeButtonCallback, - enabled = negativeButtonEnabled, - ) { - negativeButtonTitle?.let { Text(stringResource(it)) } + if (negativeButtonTitle != null) { + Spacer(modifier = Modifier.height(16.dp)) + TextButton( + modifier = Modifier.fillMaxWidth(), + onClick = negativeButtonCallback, + enabled = negativeButtonEnabled, + ) { + Text(stringResource(negativeButtonTitle)) + } } Spacer(Modifier.height(40.dp)) } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt index 9a25a60323..6c79c1b762 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt @@ -56,6 +56,10 @@ class VerifySelfSessionPresenterTests { eventSink(VerifySelfSessionViewEvents.RequestVerification) // Await for other device response: assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) + // Await for the state to be Ready + assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Ready) + // Await for other device response (again): + assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.AwaitingOtherDeviceResponse) // Finally, ChallengeReceived: val verifyingState = awaitItem() assertThat(verifyingState.verificationFlowStep).isInstanceOf(VerificationStep.Verifying::class.java) @@ -233,8 +237,8 @@ class VerifySelfSessionPresenterTests { } private suspend fun ReceiveTurbine.awaitChallengeReceivedState(): VerifySelfSessionState { - // Skip 'waiting for response' state - skipItems(1) + // Skip 'waiting for response', 'ready' and 'starting verification' state + skipItems(3) // Received challenge return awaitItem() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt index 04834da309..50fbf6f232 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt @@ -38,6 +38,7 @@ class RustSessionVerificationService @Inject constructor() : SessionVerification _isReady.value = value != null // If status was 'Unknown', move it to either 'Verified' or 'NotVerified' if (value != null) { + value.setDelegate(this) updateVerificationStatus(value.isVerified()) } } @@ -52,7 +53,6 @@ class RustSessionVerificationService @Inject constructor() : SessionVerification override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus.asStateFlow() override fun requestVerification() = tryOrFail { - verificationController?.setDelegate(this) verificationController?.requestVerification() } @@ -63,7 +63,6 @@ class RustSessionVerificationService @Inject constructor() : SessionVerification override fun declineVerification() = tryOrFail { verificationController?.declineVerification() } override fun startVerification() = tryOrFail { - verificationController?.setDelegate(this) verificationController?.startSasVerification() } diff --git a/libraries/ui-strings/src/main/res/values/strings_eax.xml b/libraries/ui-strings/src/main/res/values/strings_eax.xml index 40563c0a3b..f0f4bc1be7 100644 --- a/libraries/ui-strings/src/main/res/values/strings_eax.xml +++ b/libraries/ui-strings/src/main/res/values/strings_eax.xml @@ -47,6 +47,7 @@ I am ready Retry verification + Start They match Waiting to match diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..23942ca47e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:594ebca0307e0488bb6d22df074069181091a6310bb7fdb29a9109a10997319e +size 26874 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c9eb5f41d2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25598da9a3d84cecb4204d140609f2de5b03facd547f7ef45c53ccead333ce05 +size 26343 From ef74642cae6a21d5b95e50d94b0d6f858316bfc2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:10:06 +0100 Subject: [PATCH 44/47] Update dependency org.owasp.dependencycheck to v8.2.1 (#252) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b8ce5b521f..3bac7e6cca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ serialization_json = "1.5.0" showkase = "1.0.0-beta17" jsoup = "1.15.4" appyx = "1.0.5" -dependencycheck = "8.2.0" +dependencycheck = "8.2.1" stem = "2.3.0" sqldelight = "1.5.5" From fb0acc9f519d4a662a1f7ad40baf07ec9e4618d6 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 23 Mar 2023 15:19:18 +0100 Subject: [PATCH 45/47] Make `ChangeServerPresenter.submit` fail as expected (#236) * Make `ChangeServerPresenter.submit` fail as expected * Fix tests to prevent this from ever happening again --- .../login/impl/changeserver/ChangeServerPresenter.kt | 2 +- .../impl/changeserver/ChangeServerPresenterTest.kt | 3 +++ .../matrix/test/auth/FakeAuthenticationService.kt | 11 ++--------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt index db284e0910..f2e688812c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt @@ -69,7 +69,7 @@ class ChangeServerPresenter @Inject constructor(private val authenticationServic private fun CoroutineScope.submit(homeserverUrl: MutableState, changeServerAction: MutableState>) = launch { suspend { val domain = tryOrNull { URL(homeserverUrl.value) }?.host ?: homeserverUrl.value - authenticationService.setHomeserver(domain) + authenticationService.setHomeserver(domain).getOrThrow() homeserverUrl.value = domain }.execute(changeServerAction, errorMapping = ChangeServerError::from) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index a47a7977b4..70ecae44b8 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -134,6 +134,7 @@ class ChangeServerPresenterTest { val initialState = awaitItem() authServer.givenChangeServerError(Throwable()) initialState.eventSink.invoke(ChangeServerEvents.Submit) + skipItems(1) // Loading val failureState = awaitItem() assertThat(failureState.submitEnabled).isFalse() assertThat(failureState.changeServerAction).isInstanceOf(Async.Failure::class.java) @@ -155,6 +156,8 @@ class ChangeServerPresenterTest { authenticationService.givenChangeServerError(A_THROWABLE) initialState.eventSink(ChangeServerEvents.Submit) + skipItems(1) // Loading + // Check an error was returned val submittedState = awaitItem() assertThat(submittedState.changeServerAction).isInstanceOf(Async.Failure::class.java) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt index e44a1d18a8..886eac95ef 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt @@ -53,20 +53,13 @@ class FakeAuthenticationService : MatrixAuthenticationService { } override suspend fun setHomeserver(homeserver: String): Result { - changeServerError?.let { throw it } delay(100) - return Result.success(Unit) + return changeServerError?.let { Result.failure(it) } ?: Result.success(Unit) } override suspend fun login(username: String, password: String): Result { delay(100) - return loginError.let { loginError -> - if (loginError == null) { - Result.success(A_USER_ID) - } else { - Result.failure(loginError) - } - } + return loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) } fun givenLoginError(throwable: Throwable?) { From 7d0d076657085f80e6baa1d5dd9526af2d6499f4 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 23 Mar 2023 15:54:44 +0100 Subject: [PATCH 46/47] [Verify Session] Design fixes (#230) * Design fixes. * Make text in emoji items secondary color and the snackbar duration short again * Try to make margins smaller on smaller displays --------- Co-authored-by: Florian Renaud --- changelog.d/89.bugfix | 1 + .../features/roomlist/impl/RoomListView.kt | 4 +- .../impl/VerifySelfSessionView.kt | 54 ++++++++++++------- .../designsystem/ElementTextStyles.kt | 2 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...enDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ationHeaderDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...tionHeaderLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- 44 files changed, 119 insertions(+), 102 deletions(-) create mode 100644 changelog.d/89.bugfix diff --git a/changelog.d/89.bugfix b/changelog.d/89.bugfix new file mode 100644 index 0000000000..8c3faa2e4b --- /dev/null +++ b/changelog.d/89.bugfix @@ -0,0 +1 @@ +Design fixes for the session verification flow and button components. diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index 88209c31f6..810cb74715 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -135,7 +135,7 @@ fun RoomListContent( if (state.presentVerificationSuccessfulMessage) { snackbarHostState.showSnackbar( message = verificationCompleteMessage, - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) state.eventSink(RoomListEvents.ClearSuccessfulVerificationMessage) } @@ -194,8 +194,6 @@ fun RoomListContent( SnackbarHost (snackbarHostState) { data -> Snackbar( snackbarData = data, - containerColor = MaterialTheme.colorScheme.surfaceVariant, - contentColor = MaterialTheme.colorScheme.primary ) } }, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 31f6523919..4b51e93e81 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -23,11 +23,11 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -39,10 +39,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.flowlayout.FlowRow @@ -82,16 +85,15 @@ fun VerifySelfSessionView( derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed } } Surface { - Column(modifier = modifier.fillMaxWidth()) { + Column(modifier = modifier.systemBarsPadding()) { Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 20.dp) .weight(1f) - .verticalScroll(rememberScrollState()) + .padding(horizontal = 20.dp) ) { HeaderContent(verificationFlowStep = verificationFlowStep) - Content(flowState = verificationFlowStep) + Content(modifier = Modifier.weight(1f), flowState = verificationFlowStep) } if (buttonsVisible) { BottomMenu(screenState = state, goBack = ::goBackAndCancelIfNeeded) @@ -120,8 +122,9 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = FlowStep.AwaitingOtherDeviceResponse -> StringR.string.verification_subtitle_waiting FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> StringR.string.verification_subtitle_verifying } + Column(modifier) { - Spacer(Modifier.height(68.dp)) + Spacer(Modifier.height(80.dp)) Box( modifier = Modifier .size(width = 70.dp, height = 70.dp) @@ -164,14 +167,14 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = @Composable internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { - Column(modifier) { - Spacer(Modifier.height(56.dp)) + Column(modifier, verticalArrangement = Arrangement.Center) { + Spacer(Modifier.shrinkableHeight(min = 20.dp, max = 56.dp)) when (flowState) { FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting() is FlowStep.Verifying -> ContentVerifying(flowState) } - Spacer(Modifier.height(56.dp)) + Spacer(Modifier.shrinkableHeight(min = 20.dp, max = 56.dp)) } } @@ -185,19 +188,22 @@ internal fun ContentWaiting(modifier: Modifier = Modifier) { @Composable internal fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) { FlowRow( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()), mainAxisAlignment = MainAxisAlignment.Center, mainAxisSpacing = 32.dp, crossAxisSpacing = 40.dp ) { for (entry in verificationFlowStep.emojiList) { - Column( - modifier = Modifier.defaultMinSize(minWidth = 56.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(entry.code, fontSize = 34.sp) Spacer(modifier = Modifier.height(16.dp)) - Text(entry.name, style = ElementTextStyles.Regular.body) + Text( + entry.name, + style = ElementTextStyles.Regular.bodyMD, + color = MaterialTheme.colorScheme.secondary, + ) } } } @@ -256,14 +262,14 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) modifier = Modifier.fillMaxWidth(), onClick = { positiveButtonEvent?.let { eventSink(it) } } ) { - positiveButtonTitle?.let { Text(stringResource(it)) } + positiveButtonTitle?.let { Text(stringResource(it), style = ElementTextStyles.Button) } } } else { Button( modifier = Modifier.fillMaxWidth(), onClick = { positiveButtonEvent?.let { eventSink(it) } } ) { - positiveButtonTitle?.let { Text(stringResource(it)) } + positiveButtonTitle?.let { Text(stringResource(it), style = ElementTextStyles.Button) } } } if (negativeButtonTitle != null) { @@ -273,7 +279,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) onClick = negativeButtonCallback, enabled = negativeButtonEnabled, ) { - Text(stringResource(negativeButtonTitle)) + Text(stringResource(negativeButtonTitle), fontSize = 16.sp) } } Spacer(Modifier.height(40.dp)) @@ -297,3 +303,15 @@ private fun ContentToPreview(state: VerifySelfSessionState) { goBack = {}, ) } + +private fun Modifier.shrinkableHeight( + min: Dp, + max: Dp, + minScreenHeight: Int = 720 +): Modifier = composed { + if (LocalConfiguration.current.screenHeightDp >= minScreenHeight) { + then(Modifier.height(max)) + } else { + then(Modifier.height(min)) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt index 9af09f83db..13820ed83b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ElementTextStyles.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.unit.sp object ElementTextStyles { val Button = TextStyle( - fontSize = 17.sp, + fontSize = 16.sp, fontWeight = FontWeight.Bold, lineHeight = 22.sp, fontStyle = FontStyle.Normal, diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 82668021bf..7aa5b9b9ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47cc4075122ec2d0a1420e6c3c8765f6d28b521927034067a1ff1c877bf069fb -size 41214 +oid sha256:2fac7314c1dee8d4570922b984b0749f24ec8984e5e602892e2e4ed925d143b6 +size 41145 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 35f655bf97..bcae624eb5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8d776746230925c8b35bb688ae51d3b3eca0ebee6beb9e716dabcbf4ae57361 -size 44563 +oid sha256:dff29249aabb7818151e55dbc9a1290bc09cf0e638e3ad2842791904b14368ca +size 44381 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 2a6549e79c..79436794bb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c22ea865154e6c55cdc04acd4a96c3d0c87e1918c29604d72d37d1b29466ff46 -size 43877 +oid sha256:cb351458d7b5d70f2201f94e4466d5c7dbebb60bca164a986967d2d8006405da +size 43814 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 1f72911953..1bea49ba74 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95a4f4ce5871c6c65dfd732bde01ea3a80c47fd97a83e306642b4b3821b23b65 -size 48597 +oid sha256:4363256d328a0ab2cc3966b42827c7a7319bee962710a0bac542f6494799ff71 +size 48526 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 82fee58c8c..6740abc12a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74483873056b7dab096936e09e07249b84ffebe42619f20a829e9994b3b01ced -size 43840 +oid sha256:afa9ab67471727f4c396d1919fd1187ec28a40b8cb9b03c2b35400c5e32f4eda +size 43776 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index c92686807a..17f36f745e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c2700fd98861f5bb34ee48b24c7ddceea57971fbe9e9b0aa0d5b24edc90cc6a -size 43809 +oid sha256:6b724d1b93a04117fc3cc405920fd22530e004cc9ba9c39f88ca1a2a2a4229c4 +size 43743 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 18419dcadb..d97cb348db 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86c3bc2523fd194a87d062bb122a8d84d412db419c0a9e2f0c6e51ccd338119a -size 37809 +oid sha256:28c23058fdd9b24a7079251a2ab1ae2f85dafe0c1d4f01be3ffeca91984e0805 +size 37703 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index c32968c45e..2520176bfd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e0c3a87fb5fc5c3e8c31df3c071bf69b956e0dcfc35f5168452253c9e55b6fd -size 41535 +oid sha256:7779d6bf28eca6e72f9f1bcbd8b27881d0df0e594041269367a1f909e941cec9 +size 41368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index f54f15bda4..087add28f7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f841b8076376b61a21c517e93627992a179ef1042566133367229c4926322279 -size 40436 +oid sha256:f51052d3ece86fe7e07cc686a539b81d23f795c64bf66bd093b9d0e8e9fc7192 +size 40335 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 23d5346e68..d5c33e67c3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:091e106c67c1a503c98c36a0ed58a3cb08461840643cd2fe2ffc7e1f46ab3774 -size 47711 +oid sha256:27d2d879493e8ed1cc17dcdbb384504126fad63eb7e53a069f530457c35b94b3 +size 47609 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 494a6e911d..488e1403d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9496dd89a284f77cb01295a69dcf60ccdb566224560139d13f926e38a0bd226c -size 40436 +oid sha256:66e9060ad93cec0e1bf8e284ac959a713a8201a33ff931aeb61aaf29ab7d9640 +size 40335 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index af47554d4f..1cc8419b21 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,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.login.impl.changeserver_null_DefaultGroup_ChangeServerViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be6c0687f8ac84831db038a9846aefba81c4a6af08237d89b2cdba5676f31142 -size 40419 +oid sha256:7f7ecb805b73afe15db56853fec77d92f28fc9f8080dc7f1b2bb9f99cc262719 +size 40317 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 634ed7c29b..cb0afeb4a0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae82ef6285127f343f0ebee35cedc6f28132dde37c3303bbc33aa881c471c48c -size 32466 +oid sha256:a302148918d0f328b778c18de007538724879fd6f02babf667504ab049c02511 +size 32428 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 76a5a91ded..c14094b9a7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50627ff6140b8fb9eed181e812740a8bf8cf4cdb23cb4cf2fa9cd0b37fb880bc -size 35224 +oid sha256:1f67b9a62ee3628af0308b2a01883e09d4068c3c833a79c702a204942e2d2133 +size 35180 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png index c0db30a3a7..6ccda1c2a4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f94523e18a1eb82324325f0e3f8439d4d457d6d4883cd75a7fa6169f90bbdf4 -size 34229 +oid sha256:5c0b9edebd50e38f07ae3663f0f760674e8d9194d41077c70f93d8af46a78f0a +size 34139 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png index b36d41cb72..ff1bec8155 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe3431804d6d84b8588b0fb475bd824159abe4d0a7e35f0b7967b08477b07778 -size 33523 +oid sha256:3d552e8ad5f20ce2c08c3f7a4b970d552743b473acdc7a75d5414520a9db7421 +size 33475 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 2bb1ba397f..358e8f4902 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d1c502239f0fd9cdd7fa286275d299b74597a2b0b5d0f081503a7db3cb39f52 -size 30277 +oid sha256:f548bca0c797f35eec698f28daf74d409210d076b91ef5e4db1501ed12cefb88 +size 30197 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png index c0db30a3a7..6ccda1c2a4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,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.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f94523e18a1eb82324325f0e3f8439d4d457d6d4883cd75a7fa6169f90bbdf4 -size 34229 +oid sha256:5c0b9edebd50e38f07ae3663f0f760674e8d9194d41077c70f93d8af46a78f0a +size 34139 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_0,NEXUS_5,1.0,en].png index 044c705c0b..6f26876131 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e3e148e980f5096934ec4742a3f8b05442f0019dbcf6435fcdc1875c326a6ae -size 31513 +oid sha256:7890d36ac726a7f0764319a72d0d3aca13d2c1d382616f0f1bdb5cc19926b168 +size 31455 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png index 8093da465f..215cf8e799 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21e9c4c0ecf5a348361ff30195195743192cb784c9d6d248ca83010e3646c39f -size 34212 +oid sha256:978d1b6b40b4109a1dd941574b08d39093c5a1eeaf3da18c24a677080bfb378a +size 34158 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png index 2ea09c2a08..6feb0ddb81 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04f73ec1830baa28808881f040f6bfc2b1ce0de1ca2d4b3e16a3b57513814a7f -size 33173 +oid sha256:785ad6fe0af439444b469ae0189ea627600fc0ba71f1dff1af470ce10ce81d4e +size 33105 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png index 98896a556b..ee94587696 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da84d9195ed0cac06efff562156ed8abc161a6dbd3272727c1cb787b83c62558 -size 32253 +oid sha256:ea3ff5e912f88f12bdbf65ce027a5bde87472bbafd0146ebf609f36027dda31a +size 32194 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png index f7e513c68a..36c73ddbf0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea2dce0fde5ff6563f283e643edadd5c95c29a4084c8f1d3902ec3c759cccea7 -size 30070 +oid sha256:09ff583713751988f4ccdb465b1b53d88b788d6b275512afde08ce4e5d3e4346 +size 30022 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png index 2ea09c2a08..6feb0ddb81 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,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.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04f73ec1830baa28808881f040f6bfc2b1ce0de1ca2d4b3e16a3b57513814a7f -size 33173 +oid sha256:785ad6fe0af439444b469ae0189ea627600fc0ba71f1dff1af470ce10ce81d4e +size 33105 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,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.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png index 21f1bae12a..4264691620 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,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.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:474c799a08ccba7c8b4d9ba3d120b103d0e8bb9654bb4da576d4eee579e5d517 -size 28222 +oid sha256:5a06f563a1b006bd007c123a03486bb26ec93029e65c3a3c71a6ead01f747ddf +size 28139 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,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.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png index 902862a63b..603ef7da0e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,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.roomlist.impl_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c705a9c97a0bc5a2987cef4db6fd9b79de1cf1f5405035c6371615fb72fc5f36 -size 27715 +oid sha256:8796755d5118f24c6d3632cf1b0cee2b1d2b3743620d24e65f57ceb27bc7a48e +size 27627 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_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.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 9a5b02d262..1fa7abc24e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_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.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f653439749b16d9f683b63984855e2dad576e5d567eeed792f28e9484b12b67f -size 60599 +oid sha256:d51bcda4d9b04e7569aff45bcf599c779bbcad52de8c4700036abe4084128707 +size 60513 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_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.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 6ce2a97c89..0f011129a8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_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.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d88db44fb66340043d83e256b1eb7655e66af5e11daa55bd1336b4ff6e57883d -size 59472 +oid sha256:69f1674c3e4dc3afc194c0a7bd1064ae37722cc132ead43bf02a1183cc1e2ece +size 59392 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index f7019f8b86..e9dfd4557b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07e72c29b3a928eaa86ec7c97cb680cf2eaacebf6512390eb9dda47bdc2a9c7e -size 29249 +oid sha256:b964fd69198367cd702d8ff09d75a79a66bbee22798cbaa381fabde9bab5fda0 +size 29646 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index aaf919abe1..1a79f10ed9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76b4ac8395ac876c4ff979bd92279b9735fae581987326c6ab929e8578335c5a -size 26657 +oid sha256:0162d20f04bfdd0fdd6eb6e00b18b5774f83f07a5c7a72d20d0f51988b05dc66 +size 26899 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index fd7959efbe..08ee7998e9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca4957bb6995efbf398bf211da38cafb6b2c1693167f9c940d6a31343bfcf127 -size 61454 +oid sha256:2f38843f426b531b5ec9febae2d2d442bfdf29c9fe8dda52bf27a53c8226a9d2 +size 60873 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 3e2a239d90..8431a01af7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7590a2604bb93de8a416e7e45b6c410c27fd83481cfb08f3f41e820d22fc5582 -size 62046 +oid sha256:c97fd08e6cf3df11f285c1e94d869e492b168be339dcbe2a3a841395b9549f12 +size 61452 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 22c9cd5705..4a3156cc09 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4861b0c969a8af115ad2638b54f5893e813633a61bebb8bccafa01cfda320ea9 -size 31937 +oid sha256:15b907fd9177e6e0b7b3e0371f42899099913d40b439da2addb18afb912dc69d +size 32864 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 23942ca47e..f514a4c394 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:594ebca0307e0488bb6d22df074069181091a6310bb7fdb29a9109a10997319e -size 26874 +oid sha256:949bd00ccc555ef5e3218d37e418a45c303d7eab6af652f5d48d380b1a8e19ca +size 27302 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 32ff815fd8..b95a350e79 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ee6ee256e59932e2d6aa29c393ec30499da06c6759ed33e8298838618bbfb9f -size 28465 +oid sha256:97a422ee19417033b00cbc829f4755992be068b42d2889597ad0bba68dd18fe4 +size 29172 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 34b728f940..24cdb7b67a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:586954a4fae4405f47a02140ec0ea9c89b2f6becc1b9b852d0a8fa28e10bf089 -size 25959 +oid sha256:dd0c4a8485e00b917265b6061ee901a9f131883069621d55f3f5c9a42673046c +size 26182 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index fffb4c902d..7d5bcedbe3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:083f2c5ba49a4579015c38ffa2e0c2dd4ed04f389faa4c90cf6afa6438e08ca7 -size 59174 +oid sha256:079be728240bef9778a26f623ff19e28756921b5e30ce34d3fe360eade47789e +size 59359 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 25d0a3d145..e3d2cf70a9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed5e914779a3dcfbed0af73cace4e9141259932a892ae57ee902aec4860bd879 -size 59628 +oid sha256:76aa30f5693cdc8e654dbb5927db092da91815e83222d0f540782e1d7945542f +size 59761 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index c08447d227..283819318a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c8534d5464f3ee2551df199d18af1589f6c1e0c66292756be1e1af9deee1b17 -size 31580 +oid sha256:e782e332af35ea98bc19a2154d7d8a24aeb68636f1cba768b36599010aad1722 +size 32486 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index c9eb5f41d2..dfc1cb59b3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,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.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25598da9a3d84cecb4204d140609f2de5b03facd547f7ef45c53ccead333ce05 -size 26343 +oid sha256:06a31254e0ff962006c35a9c54ca2e6136ce51bfb267bbf61f9a941dd3720fe4 +size 26828 From f744691c155db2210f543a955c0ee0580c8f6bd0 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 23 Mar 2023 17:49:15 +0100 Subject: [PATCH 47/47] Move `StateMachine` to its own module. (#257) --- features/verifysession/impl/build.gradle.kts | 1 + .../impl/VerifySelfSessionStateMachine.kt | 2 +- libraries/statemachine/build.gradle.kts | 35 +++++++++++++++++++ .../libraries}/statemachine/StateMachine.kt | 5 ++- .../statemachine/StateMachineTests.kt | 2 +- .../kotlin/extension/DependencyHandleScope.kt | 1 + settings.gradle.kts | 1 + 7 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 libraries/statemachine/build.gradle.kts rename libraries/{core/src/main/kotlin/io/element/android/libraries/core => statemachine/src/main/kotlin/io/element/android/libraries}/statemachine/StateMachine.kt (98%) rename libraries/{core/src/test/kotlin/io/element/android/libraries/core => statemachine/src/test/kotlin/io/element/android/libraries}/statemachine/StateMachineTests.kt (99%) diff --git a/features/verifysession/impl/build.gradle.kts b/features/verifysession/impl/build.gradle.kts index ab42b9e9e5..3aca74afed 100644 --- a/features/verifysession/impl/build.gradle.kts +++ b/features/verifysession/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.statemachine) implementation(libs.accompanist.flowlayout) api(projects.features.verifysession.api) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt index 3c905334eb..8006a0b24f 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateMachine.kt @@ -17,7 +17,7 @@ @file:Suppress("WildcardImport") package io.element.android.features.verifysession.impl -import io.element.android.libraries.core.statemachine.createStateMachine +import io.element.android.libraries.statemachine.createStateMachine import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationFlowState diff --git a/libraries/statemachine/build.gradle.kts b/libraries/statemachine/build.gradle.kts new file mode 100644 index 0000000000..31fe22b5f8 --- /dev/null +++ b/libraries/statemachine/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 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. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("java-library") + id("com.android.lint") + alias(libs.plugins.kotlin.jvm) +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +dependencies { + implementation(libs.coroutines.core) + + testImplementation(libs.test.junit) + testImplementation(libs.test.truth) +} diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/statemachine/StateMachine.kt b/libraries/statemachine/src/main/kotlin/io/element/android/libraries/statemachine/StateMachine.kt similarity index 98% rename from libraries/core/src/main/kotlin/io/element/android/libraries/core/statemachine/StateMachine.kt rename to libraries/statemachine/src/main/kotlin/io/element/android/libraries/statemachine/StateMachine.kt index 81451b7e74..c1620fd97e 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/statemachine/StateMachine.kt +++ b/libraries/statemachine/src/main/kotlin/io/element/android/libraries/statemachine/StateMachine.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.element.android.libraries.core.statemachine +package io.element.android.libraries.statemachine -import io.element.android.libraries.core.bool.orFalse import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -64,7 +63,7 @@ class StateMachine( private fun findMatchingRoute(event: E): StateMachineRoute? { val routesForEvent = routes.filter { it.eventType.isInstance(event) } - return (routesForEvent.firstOrNull { it.fromState?.isInstance(currentState).orFalse() } + return (routesForEvent.firstOrNull { it.fromState?.isInstance(currentState) == true } ?: routesForEvent.firstOrNull { it.fromState == null }) as? StateMachineRoute } diff --git a/libraries/core/src/test/kotlin/io/element/android/libraries/core/statemachine/StateMachineTests.kt b/libraries/statemachine/src/test/kotlin/io/element/android/libraries/statemachine/StateMachineTests.kt similarity index 99% rename from libraries/core/src/test/kotlin/io/element/android/libraries/core/statemachine/StateMachineTests.kt rename to libraries/statemachine/src/test/kotlin/io/element/android/libraries/statemachine/StateMachineTests.kt index 2120724d79..722ed1c004 100644 --- a/libraries/core/src/test/kotlin/io/element/android/libraries/core/statemachine/StateMachineTests.kt +++ b/libraries/statemachine/src/test/kotlin/io/element/android/libraries/statemachine/StateMachineTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.core.statemachine +package io.element.android.libraries.statemachine import com.google.common.truth.Truth.assertThat import org.junit.Assert.fail diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 7ae1a0bd15..658b6bafa8 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -61,6 +61,7 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:dateformatter:impl")) implementation(project(":libraries:di")) implementation(project(":libraries:session-storage:impl")) + implementation(project(":libraries:statemachine")) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 04636090ec..8bb533a394 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -65,6 +65,7 @@ include(":libraries:encrypted-db") include(":libraries:session-storage:api") include(":libraries:session-storage:impl") include(":libraries:session-storage:impl-memory") +include(":libraries:statemachine") include(":services:analytics:api") include(":services:analytics:noop")