From 98343e05316cf124a8fc976fb52bc54d41988fa7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 8 Aug 2025 17:42:06 +0200 Subject: [PATCH] refactor (start chat) : remove useless code --- .../ConfigureRoomPresenterTest.kt} | 47 +-- .../startchat/impl/CreateRoomConfig.kt | 22 -- .../startchat/impl/CreateRoomDataStore.kt | 134 ------- .../startchat/impl/CreateRoomFlowNode.kt | 83 ----- .../startchat/impl/addpeople/AddPeopleNode.kt | 45 --- .../impl/addpeople/AddPeoplePresenter.kt | 37 -- .../AddPeopleUserListStateProvider.kt | 46 --- .../startchat/impl/addpeople/AddPeopleView.kt | 94 ----- .../impl/configureroom/ConfigureRoomEvents.kt | 23 -- .../impl/configureroom/ConfigureRoomNode.kt | 55 --- .../configureroom/ConfigureRoomPresenter.kt | 209 ----------- .../ConfigureRoomPresenterArgs.kt | 14 - .../impl/configureroom/ConfigureRoomState.kt | 30 -- .../ConfigureRoomStateProvider.kt | 102 ------ .../impl/configureroom/ConfigureRoomView.kt | 339 ------------------ .../impl/configureroom/RoomAccess.kt | 22 -- .../impl/configureroom/RoomAccessItem.kt | 25 -- .../impl/configureroom/RoomAddress.kt | 13 - .../impl/configureroom/RoomVisibilityItem.kt | 30 -- .../impl/configureroom/RoomVisibilityState.kt | 26 -- .../startchat/impl/di/CreateRoomComponent.kt | 28 -- .../startchat/impl/di/CreateRoomScope.kt | 10 - .../impl/addpeople/AddPeoplePresenterTest.kt | 50 --- .../impl/addpeople/AddPeopleViewTest.kt | 89 ----- 24 files changed, 17 insertions(+), 1556 deletions(-) rename features/{startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureBaseRoomPresenterTest.kt => createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt} (91%) delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomConfig.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomDataStore.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomFlowNode.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleNode.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenter.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleUserListStateProvider.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleView.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomEvents.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomNode.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenter.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterArgs.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomState.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomStateProvider.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomView.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccess.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccessItem.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAddress.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityItem.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityState.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomComponent.kt delete mode 100644 features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomScope.kt delete mode 100644 features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenterTest.kt delete mode 100644 features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleViewTest.kt diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureBaseRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt similarity index 91% rename from features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureBaseRoomPresenterTest.kt rename to features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt index 8eab477f1a..9e11603642 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureBaseRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterTest.kt @@ -11,9 +11,15 @@ import android.net.Uri import app.cash.turbine.TurbineTestContext import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.CreatedRoom -import io.element.android.features.invitepeople.impl.CreateRoomConfig -import io.element.android.features.invitepeople.impl.CreateRoomDataStore -import io.element.android.features.invitepeople.impl.userlist.UserListDataStore +import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvents +import io.element.android.features.createroom.impl.configureroom.ConfigureRoomPresenter +import io.element.android.features.createroom.impl.configureroom.ConfigureRoomState +import io.element.android.features.createroom.impl.configureroom.CreateRoomConfig +import io.element.android.features.createroom.impl.configureroom.CreateRoomConfigStore +import io.element.android.features.createroom.impl.configureroom.RoomAccess +import io.element.android.features.createroom.impl.configureroom.RoomAddress +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem +import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -28,7 +34,6 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper -import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity import io.element.android.libraries.mediapickers.api.PickerProvider @@ -48,8 +53,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.unmockkAll -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest @@ -67,7 +70,7 @@ private const val AN_URI_FROM_CAMERA_2 = "content://uri_from_camera_2" private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery" @RunWith(RobolectricTestRunner::class) -class ConfigureBaseRoomPresenterTest { +class ConfigureRoomPresenterTest { @get:Rule val warmUpRule = WarmUpRule() @@ -124,12 +127,11 @@ class ConfigureBaseRoomPresenterTest { @Test fun `present - state is updated when fields are changed`() = runTest { - val userListDataStore = UserListDataStore() val pickerProvider = FakePickerProvider() val permissionsPresenter = FakePermissionsPresenter() val roomAliasHelper = FakeRoomAliasHelper() val presenter = createConfigureRoomPresenter( - createRoomDataStore = CreateRoomDataStore(userListDataStore, roomAliasHelper), + dataStore = CreateRoomConfigStore(roomAliasHelper), pickerProvider = pickerProvider, permissionsPresenter = permissionsPresenter, ) @@ -138,19 +140,10 @@ class ConfigureBaseRoomPresenterTest { var expectedConfig = CreateRoomConfig() assertThat(initialState.config).isEqualTo(expectedConfig) - // Select User - val selectedUser1 = aMatrixUser() - val selectedUser2 = aMatrixUser("@id_of_bob:server.org", "Bob") - userListDataStore.selectUser(selectedUser1) - skipItems(1) - userListDataStore.selectUser(selectedUser2) - var newState = awaitItem() - expectedConfig = expectedConfig.copy(invites = persistentListOf(selectedUser1, selectedUser2)) - assertThat(newState.config).isEqualTo(expectedConfig) // Room name initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME)) - newState = awaitItem() + var newState = awaitItem() expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME) assertThat(newState.config).isEqualTo(expectedConfig) @@ -206,12 +199,6 @@ class ConfigureBaseRoomPresenterTest { ) ) assertThat(newState.config).isEqualTo(expectedConfig) - - // Remove user - newState.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(selectedUser1)) - newState = awaitItem() - expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList()) - assertThat(newState.config).isEqualTo(expectedConfig) } } @@ -263,16 +250,16 @@ class ConfigureBaseRoomPresenterTest { val matrixClient = createMatrixClient() val analyticsService = FakeAnalyticsService() val mediaPreProcessor = FakeMediaPreProcessor() - val createRoomDataStore = CreateRoomDataStore(UserListDataStore(), FakeRoomAliasHelper()) + val dataStore = CreateRoomConfigStore( FakeRoomAliasHelper()) val presenter = createConfigureRoomPresenter( - createRoomDataStore = createRoomDataStore, + dataStore = dataStore, mediaPreProcessor = mediaPreProcessor, matrixClient = matrixClient, analyticsService = analyticsService ) presenter.test { val initialState = initialState() - createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) + dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY)) skipItems(1) mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk()))) matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION)) @@ -405,7 +392,7 @@ class ConfigureBaseRoomPresenterTest { private fun createConfigureRoomPresenter( roomAliasHelper: RoomAliasHelper = FakeRoomAliasHelper(), - createRoomDataStore: CreateRoomDataStore = CreateRoomDataStore(UserListDataStore(), roomAliasHelper), + dataStore: CreateRoomConfigStore = CreateRoomConfigStore(roomAliasHelper), matrixClient: MatrixClient = createMatrixClient(), pickerProvider: PickerProvider = FakePickerProvider(), mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(), @@ -414,7 +401,7 @@ class ConfigureBaseRoomPresenterTest { isKnockFeatureEnabled: Boolean = true, mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), ) = ConfigureRoomPresenter( - dataStore = createRoomDataStore, + dataStore = dataStore, matrixClient = matrixClient, mediaPickerProvider = pickerProvider, mediaPreProcessor = mediaPreProcessor, diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomConfig.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomConfig.kt deleted file mode 100644 index 2cbd961dad..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomConfig.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl - -import android.net.Uri -import io.element.android.features.startchat.impl.configureroom.RoomVisibilityState -import io.element.android.libraries.matrix.api.user.MatrixUser -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf - -data class CreateRoomConfig( - val roomName: String? = null, - val topic: String? = null, - val avatarUri: Uri? = null, - val invites: ImmutableList = persistentListOf(), - val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, -) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomDataStore.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomDataStore.kt deleted file mode 100644 index ff0746c1b3..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomDataStore.kt +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl - -import android.net.Uri -import io.element.android.features.startchat.impl.configureroom.RoomAccess -import io.element.android.features.startchat.impl.configureroom.RoomAccessItem -import io.element.android.features.startchat.impl.configureroom.RoomAddress -import io.element.android.features.startchat.impl.configureroom.RoomVisibilityItem -import io.element.android.features.startchat.impl.configureroom.RoomVisibilityState -import io.element.android.features.startchat.impl.di.CreateRoomScope -import io.element.android.features.startchat.impl.userlist.UserListDataStore -import io.element.android.libraries.androidutils.file.safeDelete -import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.getAndUpdate -import java.io.File -import javax.inject.Inject - -@SingleIn(CreateRoomScope::class) -class CreateRoomDataStore @Inject constructor( - val selectedUserListDataStore: UserListDataStore, - private val roomAliasHelper: RoomAliasHelper, -) { - private val createRoomConfigFlow: MutableStateFlow = MutableStateFlow(CreateRoomConfig()) - private var cachedAvatarUri: Uri? = null - set(value) { - field?.path?.let { File(it) }?.safeDelete() - field = value - } - - val createRoomConfigWithInvites: Flow = combine( - selectedUserListDataStore.selectedUsers, - createRoomConfigFlow, - ) { selectedUsers, config -> - config.copy(invites = selectedUsers.toImmutableList()) - } - - fun setRoomName(roomName: String) { - createRoomConfigFlow.getAndUpdate { config -> - val newVisibility = when (config.roomVisibility) { - is RoomVisibilityState.Public -> { - val roomAddress = config.roomVisibility.roomAddress - if (roomAddress is RoomAddress.AutoFilled || roomName.isEmpty()) { - val roomAliasName = roomAliasHelper.roomAliasNameFromRoomDisplayName(roomName) - config.roomVisibility.copy( - roomAddress = RoomAddress.AutoFilled(roomAliasName), - ) - } else { - config.roomVisibility - } - } - else -> config.roomVisibility - } - config.copy( - roomName = roomName.takeIf { it.isNotEmpty() }, - roomVisibility = newVisibility, - ) - } - } - - fun setTopic(topic: String) { - createRoomConfigFlow.getAndUpdate { config -> - config.copy(topic = topic.takeIf { it.isNotEmpty() }) - } - } - - fun setAvatarUri(uri: Uri?, cached: Boolean = false) { - cachedAvatarUri = uri.takeIf { cached } - createRoomConfigFlow.getAndUpdate { config -> - config.copy(avatarUri = uri) - } - } - - fun setRoomVisibility(visibility: RoomVisibilityItem) { - createRoomConfigFlow.getAndUpdate { config -> - config.copy( - roomVisibility = when (visibility) { - RoomVisibilityItem.Private -> RoomVisibilityState.Private - RoomVisibilityItem.Public -> { - val roomAliasName = roomAliasHelper.roomAliasNameFromRoomDisplayName(config.roomName.orEmpty()) - RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled(roomAliasName), - roomAccess = RoomAccess.Anyone, - ) - } - } - ) - } - } - - fun setRoomAddress(address: String) { - createRoomConfigFlow.getAndUpdate { config -> - config.copy( - roomVisibility = when (config.roomVisibility) { - is RoomVisibilityState.Public -> { - val sanitizedAddress = address.lowercase() - config.roomVisibility.copy(roomAddress = RoomAddress.Edited(sanitizedAddress)) - } - else -> config.roomVisibility - } - ) - } - } - - fun setRoomAccess(access: RoomAccessItem) { - createRoomConfigFlow.getAndUpdate { config -> - config.copy( - roomVisibility = when (config.roomVisibility) { - is RoomVisibilityState.Public -> { - when (access) { - RoomAccessItem.Anyone -> config.roomVisibility.copy(roomAccess = RoomAccess.Anyone) - RoomAccessItem.AskToJoin -> config.roomVisibility.copy(roomAccess = RoomAccess.Knocking) - } - } - else -> config.roomVisibility - } - ) - } - } - - fun clearCachedData() { - cachedAvatarUri = null - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomFlowNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomFlowNode.kt deleted file mode 100644 index 1124314250..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/CreateRoomFlowNode.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl - -import android.os.Parcelable -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 com.bumble.appyx.core.plugin.plugins -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.startchat.StartChatNavigator -import io.element.android.features.startchat.impl.addpeople.AddPeopleNode -import io.element.android.features.startchat.impl.configureroom.ConfigureRoomNode -import io.element.android.features.startchat.impl.di.CreateRoomComponent -import io.element.android.libraries.architecture.BackstackView -import io.element.android.libraries.architecture.BaseFlowNode -import io.element.android.libraries.architecture.bindings -import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.di.DaggerComponentOwner -import io.element.android.libraries.di.SessionScope -import kotlinx.parcelize.Parcelize - -@ContributesNode(SessionScope::class) -class CreateRoomFlowNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, -) : DaggerComponentOwner, - BaseFlowNode( - backstack = BackStack( - initialElement = NavTarget.ConfigureRoom, - savedStateMap = buildContext.savedStateMap, - ), - buildContext = buildContext, - plugins = plugins - ) { - private val component by lazy { - parent!!.bindings().createRoomComponentBuilder().build() - } - private val navigator = plugins().first() - - override val daggerComponent: Any - get() = component - - sealed interface NavTarget : Parcelable { - @Parcelize - data object AddPeople : NavTarget - - @Parcelize - data object ConfigureRoom : NavTarget - } - - override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { - return when (navTarget) { - NavTarget.AddPeople -> { - val callback = object : AddPeopleNode.Callback { - override fun onContinue() { - backstack.push(NavTarget.ConfigureRoom) - } - } - createNode(buildContext = buildContext, plugins = listOf(callback)) - } - NavTarget.ConfigureRoom -> { - createNode(buildContext = buildContext, plugins = listOf(navigator)) - } - } - } - - @Composable - override fun View(modifier: Modifier) { - BackstackView() - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleNode.kt deleted file mode 100644 index 441ba8cc76..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleNode.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -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 com.bumble.appyx.core.plugin.plugins -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.startchat.impl.di.CreateRoomScope - -@ContributesNode(CreateRoomScope::class) -class AddPeopleNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: AddPeoplePresenter, -) : Node(buildContext, plugins = plugins) { - interface Callback : Plugin { - fun onContinue() - } - - private fun onContinue() { - plugins().forEach { it.onContinue() } - } - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - AddPeopleView( - state = state, - modifier = modifier, - onBackClick = this::navigateUp, - onSkipClick = this::onContinue, - ) - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenter.kt deleted file mode 100644 index aacd80fca3..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenter.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -import androidx.compose.runtime.Composable -import io.element.android.features.startchat.impl.CreateRoomDataStore -import io.element.android.features.startchat.impl.userlist.SelectionMode -import io.element.android.features.startchat.impl.userlist.UserListPresenter -import io.element.android.features.startchat.impl.userlist.UserListPresenterArgs -import io.element.android.features.startchat.impl.userlist.UserListState -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.usersearch.api.UserRepository -import javax.inject.Inject - -class AddPeoplePresenter @Inject constructor( - userListPresenterFactory: UserListPresenter.Factory, - userRepository: UserRepository, - dataStore: CreateRoomDataStore, -) : Presenter { - private val userListPresenter = userListPresenterFactory.create( - UserListPresenterArgs( - selectionMode = SelectionMode.Multiple, - ), - userRepository, - dataStore.selectedUserListDataStore, - ) - - @Composable - override fun present(): UserListState { - return userListPresenter.present() - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleUserListStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleUserListStateProvider.kt deleted file mode 100644 index 57b3a93d57..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleUserListStateProvider.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.startchat.impl.userlist.SelectionMode -import io.element.android.features.startchat.impl.userlist.UserListState -import io.element.android.features.startchat.impl.userlist.aRecentDirectRoomList -import io.element.android.features.startchat.impl.userlist.aUserListState -import io.element.android.libraries.designsystem.theme.components.SearchBarResultState -import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import io.element.android.libraries.usersearch.api.UserSearchResult -import kotlinx.collections.immutable.toImmutableList - -open class AddPeopleUserListStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aUserListState(), - aUserListState( - searchResults = SearchBarResultState.Results(aMatrixUserList().toImmutableList()), - selectedUsers = aMatrixUserList().toImmutableList(), - isSearchActive = false, - selectionMode = SelectionMode.Multiple, - ), - aUserListState( - searchResults = SearchBarResultState.Results( - aMatrixUserList() - .mapIndexed { index, matrixUser -> - UserSearchResult(matrixUser, index % 2 == 0) - } - .toImmutableList() - ), - selectedUsers = aMatrixUserList().toImmutableList(), - isSearchActive = true, - selectionMode = SelectionMode.Multiple, - ), - aUserListState( - recentDirectRooms = aRecentDirectRoomList(), - ), - ) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleView.kt deleted file mode 100644 index ac457955d5..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleView.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -import androidx.compose.foundation.layout.consumeWindowInsets -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.tooling.preview.PreviewParameter -import io.element.android.features.startchat.impl.R -import io.element.android.features.startchat.impl.components.UserListView -import io.element.android.features.startchat.impl.userlist.UserListEvents -import io.element.android.features.startchat.impl.userlist.UserListState -import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.ui.strings.CommonStrings - -@Composable -fun AddPeopleView( - state: UserListState, - onBackClick: () -> Unit, - onSkipClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - modifier = modifier, - topBar = { - AddPeopleViewTopBar( - hasSelectedUsers = state.selectedUsers.isNotEmpty(), - onBackClick = { - if (state.isSearchActive) { - state.eventSink(UserListEvents.OnSearchActiveChanged(false)) - } else { - onBackClick() - } - }, - onNextClick = onSkipClick, - ) - } - ) { padding -> - UserListView( - modifier = Modifier - .fillMaxSize() - .padding(padding) - .consumeWindowInsets(padding), - state = state, - showBackButton = false, - onSelectUser = {}, - onDeselectUser = {}, - ) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun AddPeopleViewTopBar( - hasSelectedUsers: Boolean, - onBackClick: () -> Unit, - onNextClick: () -> Unit, -) { - TopAppBar( - titleStr = stringResource(id = R.string.screen_create_room_add_people_title), - navigationIcon = { BackButton(onClick = onBackClick) }, - actions = { - val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip - TextButton( - text = stringResource(id = textActionResId), - onClick = onNextClick, - ) - } - ) -} - -@PreviewsDayNight -@Composable -internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview { - AddPeopleView( - state = state, - onBackClick = {}, - onSkipClick = {}, - ) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomEvents.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomEvents.kt deleted file mode 100644 index 13ce502559..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomEvents.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.ui.media.AvatarAction - -sealed interface ConfigureRoomEvents { - data class RoomNameChanged(val name: String) : ConfigureRoomEvents - data class TopicChanged(val topic: String) : ConfigureRoomEvents - data class RoomVisibilityChanged(val visibilityItem: RoomVisibilityItem) : ConfigureRoomEvents - data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents - data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents - data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents - data object CreateRoom : ConfigureRoomEvents - data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents - data object CancelCreateRoom : ConfigureRoomEvents -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomNode.kt deleted file mode 100644 index 3251fbff8d..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomNode.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.bumble.appyx.core.lifecycle.subscribe -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 im.vector.app.features.analytics.plan.MobileScreen -import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.startchat.StartChatNavigator -import io.element.android.features.startchat.impl.di.CreateRoomScope -import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias -import io.element.android.services.analytics.api.AnalyticsService - -@ContributesNode(CreateRoomScope::class) -class ConfigureRoomNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val presenter: ConfigureRoomPresenter, - private val analyticsService: AnalyticsService, -) : Node(buildContext, plugins = plugins) { - private val navigator = plugins().first() - - init { - lifecycle.subscribe( - onResume = { - analyticsService.screen(MobileScreen(screenName = MobileScreen.ScreenName.CreateRoom)) - } - ) - } - - @Composable - override fun View(modifier: Modifier) { - val state = presenter.present() - ConfigureRoomView( - state = state, - modifier = modifier, - onBackClick = this::navigateUp, - onCreateRoomSuccess = { - navigator.onOpenRoom(roomIdOrAlias = it.toRoomIdOrAlias(), serverNames = emptyList()) - }, - ) - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenter.kt deleted file mode 100644 index a715bf568d..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenter.kt +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import android.net.Uri -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import im.vector.app.features.analytics.plan.CreatedRoom -import io.element.android.features.startchat.impl.CreateRoomConfig -import io.element.android.features.startchat.impl.CreateRoomDataStore -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runCatchingUpdatingState -import io.element.android.libraries.core.mimetype.MimeTypes -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters -import io.element.android.libraries.matrix.api.createroom.RoomPreset -import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper -import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility -import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility -import io.element.android.libraries.matrix.ui.media.AvatarAction -import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity -import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidityEffect -import io.element.android.libraries.mediapickers.api.PickerProvider -import io.element.android.libraries.mediaupload.api.MediaPreProcessor -import io.element.android.libraries.permissions.api.PermissionsEvents -import io.element.android.libraries.permissions.api.PermissionsPresenter -import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject -import kotlin.jvm.optionals.getOrDefault - -class ConfigureRoomPresenter @Inject constructor( - private val dataStore: CreateRoomDataStore, - private val matrixClient: MatrixClient, - private val mediaPickerProvider: PickerProvider, - private val mediaPreProcessor: MediaPreProcessor, - private val analyticsService: AnalyticsService, - permissionsPresenterFactory: PermissionsPresenter.Factory, - private val featureFlagService: FeatureFlagService, - private val roomAliasHelper: RoomAliasHelper, -) : Presenter { - private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA) - private var pendingPermissionRequest = false - - @Composable - override fun present(): ConfigureRoomState { - val cameraPermissionState = cameraPermissionPresenter.present() - val createRoomConfig by dataStore.createRoomConfigWithInvites.collectAsState(CreateRoomConfig()) - val homeserverName = remember { matrixClient.userIdServerName() } - val isKnockFeatureEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock) - }.collectAsState(initial = false) - val roomAddressValidity = remember { - mutableStateOf(RoomAddressValidity.Unknown) - } - - val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( - onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) }, - ) - val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( - onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri) } - ) - - val avatarActions by remember(createRoomConfig.avatarUri) { - derivedStateOf { - listOfNotNull( - AvatarAction.TakePhoto, - AvatarAction.ChoosePhoto, - AvatarAction.Remove.takeIf { createRoomConfig.avatarUri != null }, - ).toImmutableList() - } - } - - LaunchedEffect(cameraPermissionState.permissionGranted) { - if (cameraPermissionState.permissionGranted && pendingPermissionRequest) { - pendingPermissionRequest = false - cameraPhotoPicker.launch() - } - } - - RoomAddressValidityEffect( - client = matrixClient, - roomAliasHelper = roomAliasHelper, - newRoomAddress = createRoomConfig.roomVisibility.roomAddress().getOrDefault(""), - knownRoomAddress = null, - ) { newRoomAddressValidity -> - roomAddressValidity.value = newRoomAddressValidity - } - - val localCoroutineScope = rememberCoroutineScope() - val createRoomAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - - fun createRoom(config: CreateRoomConfig) { - createRoomAction.value = AsyncAction.Uninitialized - localCoroutineScope.createRoom(config, createRoomAction) - } - - fun handleEvents(event: ConfigureRoomEvents) { - when (event) { - is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) - is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) - is ConfigureRoomEvents.RoomVisibilityChanged -> dataStore.setRoomVisibility(event.visibilityItem) - is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) - is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess) - is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress) - is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig) - is ConfigureRoomEvents.HandleAvatarAction -> { - when (event.action) { - AvatarAction.ChoosePhoto -> galleryImagePicker.launch() - AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) { - cameraPhotoPicker.launch() - } else { - pendingPermissionRequest = true - cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) - } - AvatarAction.Remove -> dataStore.setAvatarUri(uri = null) - } - } - - ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncAction.Uninitialized - } - } - - return ConfigureRoomState( - isKnockFeatureEnabled = isKnockFeatureEnabled, - config = createRoomConfig, - avatarActions = avatarActions, - createRoomAction = createRoomAction.value, - cameraPermissionState = cameraPermissionState, - homeserverName = homeserverName, - roomAddressValidity = roomAddressValidity.value, - eventSink = ::handleEvents, - ) - } - - private fun CoroutineScope.createRoom( - config: CreateRoomConfig, - createRoomAction: MutableState> - ) = launch { - suspend { - val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } - val params = if (config.roomVisibility is RoomVisibilityState.Public) { - CreateRoomParameters( - name = config.roomName, - topic = config.topic, - isEncrypted = false, - isDirect = false, - visibility = RoomVisibility.Public, - joinRuleOverride = config.roomVisibility.roomAccess.toJoinRule(), - preset = RoomPreset.PUBLIC_CHAT, - invite = config.invites.map { it.userId }, - avatar = avatarUrl, - roomAliasName = config.roomVisibility.roomAddress() - ) - } else { - CreateRoomParameters( - name = config.roomName, - topic = config.topic, - isEncrypted = config.roomVisibility is RoomVisibilityState.Private, - isDirect = false, - visibility = RoomVisibility.Private, - historyVisibilityOverride = RoomHistoryVisibility.Invited, - preset = RoomPreset.PRIVATE_CHAT, - invite = config.invites.map { it.userId }, - avatar = avatarUrl, - ) - } - matrixClient.createRoom(params) - .onFailure { failure -> - Timber.e(failure, "Failed to create room") - } - .onSuccess { - dataStore.clearCachedData() - analyticsService.capture(CreatedRoom(isDM = false)) - } - .getOrThrow() - }.runCatchingUpdatingState(createRoomAction) - } - - private suspend fun uploadAvatar(avatarUri: Uri): String { - val preprocessed = mediaPreProcessor.process( - uri = avatarUri, - mimeType = MimeTypes.Jpeg, - deleteOriginal = false, - compressIfPossible = false, - ).getOrThrow() - val byteArray = preprocessed.file.readBytes() - return matrixClient.uploadMedia(MimeTypes.Jpeg, byteArray, null).getOrThrow() - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterArgs.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterArgs.kt deleted file mode 100644 index 2ee98b2a35..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomPresenterArgs.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import io.element.android.libraries.matrix.api.user.MatrixUser - -data class ConfigureRoomPresenterArgs( - val selectedUsers: List, -) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomState.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomState.kt deleted file mode 100644 index e94066caed..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomState.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import io.element.android.features.startchat.impl.CreateRoomConfig -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.ui.media.AvatarAction -import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity -import io.element.android.libraries.permissions.api.PermissionsState -import kotlinx.collections.immutable.ImmutableList - -data class ConfigureRoomState( - val isKnockFeatureEnabled: Boolean, - val config: CreateRoomConfig, - val avatarActions: ImmutableList, - val createRoomAction: AsyncAction, - val cameraPermissionState: PermissionsState, - val roomAddressValidity: RoomAddressValidity, - val homeserverName: String, - val eventSink: (ConfigureRoomEvents) -> Unit -) { - val isValid: Boolean = config.roomName?.isNotEmpty() == true && - (config.roomVisibility is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomStateProvider.kt deleted file mode 100644 index 09d801440b..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomStateProvider.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.startchat.impl.CreateRoomConfig -import io.element.android.libraries.architecture.AsyncAction -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.ui.components.aMatrixUserList -import io.element.android.libraries.matrix.ui.media.AvatarAction -import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity -import io.element.android.libraries.permissions.api.PermissionsState -import io.element.android.libraries.permissions.api.aPermissionsState -import kotlinx.collections.immutable.toImmutableList - -open class ConfigureRoomStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - aConfigureRoomState(), - aConfigureRoomState( - isKnockFeatureEnabled = false, - config = CreateRoomConfig( - roomName = "Room 101", - topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", - invites = aMatrixUserList().toImmutableList(), - roomVisibility = RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled("Room-101"), - roomAccess = RoomAccess.Knocking, - ), - ), - ), - aConfigureRoomState( - config = CreateRoomConfig( - roomName = "Room 101", - topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", - invites = aMatrixUserList().toImmutableList(), - roomVisibility = RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled("Room-101"), - roomAccess = RoomAccess.Knocking, - ), - ), - ), - aConfigureRoomState( - config = CreateRoomConfig( - roomName = "Room 101", - topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", - roomVisibility = RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled("Room-101"), - roomAccess = RoomAccess.Knocking, - ), - ), - roomAddressValidity = RoomAddressValidity.NotAvailable, - ), - aConfigureRoomState( - config = CreateRoomConfig( - roomName = "Room 101", - topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", - roomVisibility = RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled("Room-101"), - roomAccess = RoomAccess.Knocking, - ), - ), - roomAddressValidity = RoomAddressValidity.InvalidSymbols, - ), - aConfigureRoomState( - config = CreateRoomConfig( - roomName = "Room 101", - topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines", - roomVisibility = RoomVisibilityState.Public( - roomAddress = RoomAddress.AutoFilled("Room-101"), - roomAccess = RoomAccess.Knocking, - ), - ), - roomAddressValidity = RoomAddressValidity.Valid, - ), - ) -} - -fun aConfigureRoomState( - config: CreateRoomConfig = CreateRoomConfig(), - isKnockFeatureEnabled: Boolean = true, - avatarActions: List = emptyList(), - createRoomAction: AsyncAction = AsyncAction.Uninitialized, - cameraPermissionState: PermissionsState = aPermissionsState(showDialog = false), - homeserverName: String = "matrix.org", - roomAddressValidity: RoomAddressValidity = RoomAddressValidity.Valid, - eventSink: (ConfigureRoomEvents) -> Unit = { }, -) = ConfigureRoomState( - config = config, - isKnockFeatureEnabled = isKnockFeatureEnabled, - avatarActions = avatarActions.toImmutableList(), - createRoomAction = createRoomAction, - cameraPermissionState = cameraPermissionState, - homeserverName = homeserverName, - roomAddressValidity = roomAddressValidity, - eventSink = eventSink, -) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomView.kt deleted file mode 100644 index c056570913..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/ConfigureRoomView.kt +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import android.net.Uri -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.selection.selectableGroup -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardCapitalization -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.features.startchat.impl.R -import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage -import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom -import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize -import io.element.android.libraries.designsystem.components.async.AsyncActionView -import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults -import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.components.avatar.AvatarType -import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.list.ListItemContent -import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap -import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight -import io.element.android.libraries.designsystem.theme.components.ListItem -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.designsystem.theme.components.TextField -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet -import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList -import io.element.android.libraries.matrix.ui.components.UnsavedAvatar -import io.element.android.libraries.matrix.ui.room.address.RoomAddressField -import io.element.android.libraries.permissions.api.PermissionsView -import io.element.android.libraries.ui.strings.CommonStrings - -@Composable -fun ConfigureRoomView( - state: ConfigureRoomState, - onBackClick: () -> Unit, - onCreateRoomSuccess: (RoomId) -> Unit, - modifier: Modifier = Modifier, -) { - val focusManager = LocalFocusManager.current - val isAvatarActionsSheetVisible = remember { mutableStateOf(false) } - - fun onAvatarClick() { - focusManager.clearFocus() - isAvatarActionsSheetVisible.value = true - } - - Scaffold( - modifier = modifier.clearFocusOnTap(focusManager), - topBar = { - ConfigureRoomToolbar( - isNextActionEnabled = state.isValid, - onBackClick = onBackClick, - onNextClick = { - focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.CreateRoom) - }, - ) - } - ) { padding -> - Column( - modifier = Modifier - .padding(padding) - .imePadding() - .verticalScroll(rememberScrollState()) - .consumeWindowInsets(padding), - verticalArrangement = Arrangement.spacedBy(24.dp), - ) { - RoomNameWithAvatar( - modifier = Modifier.padding(horizontal = 16.dp), - avatarUri = state.config.avatarUri, - roomName = state.config.roomName.orEmpty(), - onAvatarClick = ::onAvatarClick, - onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, - ) - RoomTopic( - modifier = Modifier.padding(horizontal = 16.dp), - topic = state.config.topic.orEmpty(), - onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, - ) - if (state.config.invites.isNotEmpty()) { - SelectedUsersRowList( - contentPadding = PaddingValues(horizontal = 24.dp), - selectedUsers = state.config.invites, - onUserRemove = { - focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(it)) - }, - ) - } - RoomVisibilityOptions( - selected = when (state.config.roomVisibility) { - is RoomVisibilityState.Private -> RoomVisibilityItem.Private - is RoomVisibilityState.Public -> RoomVisibilityItem.Public - }, - onOptionClick = { - focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it)) - }, - ) - if (state.config.roomVisibility is RoomVisibilityState.Public && state.isKnockFeatureEnabled) { - RoomAccessOptions( - selected = when (state.config.roomVisibility.roomAccess) { - RoomAccess.Anyone -> RoomAccessItem.Anyone - RoomAccess.Knocking -> RoomAccessItem.AskToJoin - }, - onOptionClick = { - focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it)) - }, - ) - RoomAddressField( - modifier = Modifier.padding(horizontal = 16.dp), - address = state.config.roomVisibility.roomAddress.value, - homeserverName = state.homeserverName, - addressValidity = state.roomAddressValidity, - onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) }, - label = stringResource(R.string.screen_create_room_room_address_section_title), - supportingText = stringResource(R.string.screen_create_room_room_address_section_footer), - ) - Spacer(Modifier) - } - } - } - - AvatarActionBottomSheet( - actions = state.avatarActions, - isVisible = isAvatarActionsSheetVisible.value, - onDismiss = { isAvatarActionsSheetVisible.value = false }, - onSelectAction = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) } - ) - - AsyncActionView( - async = state.createRoomAction, - progressDialog = { - AsyncActionViewDefaults.ProgressDialog( - progressText = stringResource(CommonStrings.common_creating_room), - ) - }, - onSuccess = { onCreateRoomSuccess(it) }, - errorMessage = { stringResource(R.string.screen_create_room_error_creating_room) }, - onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom) }, - onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) }, - ) - - PermissionsView( - state = state.cameraPermissionState, - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun ConfigureRoomToolbar( - isNextActionEnabled: Boolean, - onBackClick: () -> Unit, - onNextClick: () -> Unit, -) { - TopAppBar( - titleStr = stringResource(R.string.screen_create_room_title), - navigationIcon = { BackButton(onClick = onBackClick) }, - actions = { - TextButton( - text = stringResource(CommonStrings.action_create), - enabled = isNextActionEnabled, - onClick = onNextClick, - ) - } - ) -} - -@Composable -private fun RoomNameWithAvatar( - avatarUri: Uri?, - roomName: String, - onAvatarClick: () -> Unit, - onChangeRoomName: (String) -> Unit, - modifier: Modifier = Modifier, -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - UnsavedAvatar( - avatarUri = avatarUri, - avatarSize = AvatarSize.EditRoomDetails, - avatarType = AvatarType.Room(), - modifier = Modifier.clickable(onClick = onAvatarClick), - ) - - TextField( - label = stringResource(R.string.screen_create_room_room_name_label), - value = roomName, - placeholder = stringResource(CommonStrings.common_room_name_placeholder), - singleLine = true, - onValueChange = onChangeRoomName, - ) - } -} - -@Composable -private fun RoomTopic( - topic: String, - onTopicChange: (String) -> Unit, - modifier: Modifier = Modifier, -) { - TextField( - modifier = modifier, - label = stringResource(R.string.screen_create_room_topic_label), - value = topic, - onValueChange = onTopicChange, - maxLines = 3, - supportingText = stringResource(CommonStrings.common_topic_placeholder), - keyboardOptions = KeyboardOptions( - capitalization = KeyboardCapitalization.Sentences, - ), - ) -} - -@Composable -private fun ConfigureRoomOptions( - title: String, - modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit, -) { - Column( - modifier = modifier.selectableGroup() - ) { - Text( - text = title, - style = ElementTheme.typography.fontBodyLgMedium, - color = ElementTheme.colors.textPrimary, - modifier = Modifier.padding(horizontal = 16.dp), - ) - content() - } -} - -@Composable -private fun RoomVisibilityOptions( - selected: RoomVisibilityItem, - onOptionClick: (RoomVisibilityItem) -> Unit, - modifier: Modifier = Modifier, -) { - ConfigureRoomOptions( - title = stringResource(R.string.screen_create_room_room_visibility_section_title), - modifier = modifier, - ) { - RoomVisibilityItem.entries.forEach { item -> - val isSelected = item == selected - ListItem( - leadingContent = ListItemContent.Custom { - RoundedIconAtom( - size = RoundedIconAtomSize.Big, - resourceId = item.icon, - tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary, - ) - }, - headlineContent = { Text(text = stringResource(item.title)) }, - supportingContent = { Text(text = stringResource(item.description)) }, - trailingContent = ListItemContent.RadioButton(selected = isSelected), - onClick = { onOptionClick(item) }, - ) - } - } -} - -@Composable -private fun RoomAccessOptions( - selected: RoomAccessItem, - onOptionClick: (RoomAccessItem) -> Unit, - modifier: Modifier = Modifier, -) { - ConfigureRoomOptions( - title = stringResource(R.string.screen_create_room_room_access_section_header), - modifier = modifier, - ) { - RoomAccessItem.entries.forEach { item -> - ListItem( - headlineContent = { Text(text = stringResource(item.title)) }, - supportingContent = { Text(text = stringResource(item.description)) }, - trailingContent = ListItemContent.RadioButton(selected = item == selected), - onClick = { onOptionClick(item) }, - ) - } - } -} - -@PreviewWithLargeHeight -@Composable -internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = - ElementPreviewLight { ContentToPreview(state) } - -@PreviewWithLargeHeight -@Composable -internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = - ElementPreviewDark { ContentToPreview(state) } - -@ExcludeFromCoverage -@Composable -private fun ContentToPreview(state: ConfigureRoomState) { - ConfigureRoomView( - state = state, - onBackClick = {}, - onCreateRoomSuccess = {}, - ) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccess.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccess.kt deleted file mode 100644 index bf75d9605e..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccess.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import io.element.android.libraries.matrix.api.room.join.JoinRule - -enum class RoomAccess { - Anyone, - Knocking -} - -fun RoomAccess.toJoinRule(): JoinRule? { - return when (this) { - RoomAccess.Anyone -> null - RoomAccess.Knocking -> JoinRule.Knock - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccessItem.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccessItem.kt deleted file mode 100644 index 0a48afe849..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAccessItem.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import androidx.annotation.StringRes -import io.element.android.features.startchat.impl.R - -enum class RoomAccessItem( - @StringRes val title: Int, - @StringRes val description: Int -) { - Anyone( - title = R.string.screen_create_room_room_access_section_anyone_option_title, - description = R.string.screen_create_room_room_access_section_anyone_option_description, - ), - AskToJoin( - title = R.string.screen_create_room_room_access_section_knocking_option_title, - description = R.string.screen_create_room_room_access_section_knocking_option_description, - ), -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAddress.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAddress.kt deleted file mode 100644 index 51e02d881a..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomAddress.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -sealed class RoomAddress(open val value: String) { - data class AutoFilled(override val value: String) : RoomAddress(value) - data class Edited(override val value: String) : RoomAddress(value) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityItem.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityItem.kt deleted file mode 100644 index 4ca8b51601..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityItem.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import io.element.android.features.startchat.impl.R -import io.element.android.libraries.designsystem.icons.CompoundDrawables - -enum class RoomVisibilityItem( - @DrawableRes val icon: Int, - @StringRes val title: Int, - @StringRes val description: Int -) { - Private( - icon = CompoundDrawables.ic_compound_lock, - title = R.string.screen_create_room_private_option_title, - description = R.string.screen_create_room_private_option_description, - ), - Public( - icon = CompoundDrawables.ic_compound_public, - title = R.string.screen_create_room_public_option_title, - description = R.string.screen_create_room_public_option_description, - ) -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityState.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityState.kt deleted file mode 100644 index 5a9a3126f6..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/configureroom/RoomVisibilityState.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.configureroom - -import java.util.Optional - -sealed interface RoomVisibilityState { - data object Private : RoomVisibilityState - - data class Public( - val roomAddress: RoomAddress, - val roomAccess: RoomAccess, - ) : RoomVisibilityState - - fun roomAddress(): Optional { - return when (this) { - is Private -> Optional.empty() - is Public -> Optional.of(roomAddress.value) - } - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomComponent.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomComponent.kt deleted file mode 100644 index 6f6569bcb3..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomComponent.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.di - -import com.squareup.anvil.annotations.ContributesTo -import com.squareup.anvil.annotations.MergeSubcomponent -import io.element.android.libraries.architecture.NodeFactoriesBindings -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.di.SingleIn - -@SingleIn(CreateRoomScope::class) -@MergeSubcomponent(CreateRoomScope::class) -interface CreateRoomComponent : NodeFactoriesBindings { - @MergeSubcomponent.Builder - interface Builder { - fun build(): CreateRoomComponent - } - - @ContributesTo(SessionScope::class) - interface ParentBindings { - fun createRoomComponentBuilder(): Builder - } -} diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomScope.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomScope.kt deleted file mode 100644 index a164ba50b8..0000000000 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/di/CreateRoomScope.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2023, 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.di - -abstract class CreateRoomScope private constructor() diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenterTest.kt deleted file mode 100644 index d5742b2a26..0000000000 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeoplePresenterTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test -import com.google.common.truth.Truth.assertThat -import io.element.android.features.invitepeople.impl.CreateRoomDataStore -import io.element.android.features.startchat.impl.userlist.FakeUserListPresenterFactory -import io.element.android.features.invitepeople.impl.userlist.UserListDataStore -import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper -import io.element.android.libraries.usersearch.test.FakeUserRepository -import io.element.android.tests.testutils.WarmUpRule -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -class AddPeoplePresenterTest { - @get:Rule - val warmUpRule = WarmUpRule() - - private lateinit var presenter: AddPeoplePresenter - - @Before - fun setup() { - presenter = AddPeoplePresenter( - FakeUserListPresenterFactory(), - FakeUserRepository(), - CreateRoomDataStore(UserListDataStore(), FakeRoomAliasHelper()) - ) - } - - @Test - fun `present - initial state`() = runTest { - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - // TODO This doesn't actually test anything... - val initialState = awaitItem() - assertThat(initialState) - } - } -} diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleViewTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleViewTest.kt deleted file mode 100644 index 9d8af8d61d..0000000000 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/addpeople/AddPeopleViewTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.startchat.impl.addpeople - -import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.features.invitepeople.impl.userlist.UserListEvents -import io.element.android.features.invitepeople.impl.userlist.UserListState -import io.element.android.features.invitepeople.impl.userlist.aUserListState -import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.tests.testutils.EnsureNeverCalled -import io.element.android.tests.testutils.EventsRecorder -import io.element.android.tests.testutils.clickOn -import io.element.android.tests.testutils.ensureCalledOnce -import io.element.android.tests.testutils.pressBack -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class AddPeopleViewTest { - @get:Rule - val rule = createAndroidComposeRule() - - @Test - fun `clicking on back invokes the expected callback`() { - val eventsRecorder = EventsRecorder() - ensureCalledOnce { - rule.setAddPeopleView( - aUserListState( - eventSink = eventsRecorder, - ), - onBackClick = it - ) - rule.pressBack() - } - eventsRecorder.assertSingle(UserListEvents.UpdateSearchQuery("")) - } - - @Test - fun `clicking on back during search emits the expected Event`() { - val eventsRecorder = EventsRecorder() - rule.setAddPeopleView( - aUserListState( - isSearchActive = true, - eventSink = eventsRecorder, - ), - ) - rule.pressBack() - eventsRecorder.assertSingle(UserListEvents.OnSearchActiveChanged(false)) - } - - @Test - fun `clicking on skip invokes the expected callback`() { - val eventsRecorder = EventsRecorder() - ensureCalledOnce { - rule.setAddPeopleView( - aUserListState( - eventSink = eventsRecorder, - ), - onNextClick = it - ) - rule.clickOn(CommonStrings.action_skip) - } - eventsRecorder.assertSingle(UserListEvents.UpdateSearchQuery("")) - } -} - -private fun AndroidComposeTestRule.setAddPeopleView( - state: UserListState, - onBackClick: () -> Unit = EnsureNeverCalled(), - onNextClick: () -> Unit = EnsureNeverCalled(), -) { - setContent { - AddPeopleView( - state = state, - onBackClick = onBackClick, - onSkipClick = onNextClick, - ) - } -}