From 6450fc572482202981de2085163a16d5c31eec26 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Fri, 24 Mar 2023 16:01:14 +0100 Subject: [PATCH] Create or retrieve DM --- .../android/appnav/LoggedInFlowNode.kt | 12 ++++- features/createroom/api/build.gradle.kts | 1 + .../createroom/api/CreateRoomEntryPoint.kt | 19 +++++++- .../createroom/impl/CreateRoomFlowNode.kt | 7 +++ .../impl/DefaultCreateRoomEntryPoint.kt | 19 +++++++- .../impl/root/CreateRoomRootEvents.kt | 4 +- .../impl/root/CreateRoomRootNode.kt | 20 ++++---- .../impl/root/CreateRoomRootPresenter.kt | 46 +++++++++++++++++-- .../impl/root/CreateRoomRootState.kt | 4 ++ .../impl/root/CreateRoomRootStateProvider.kt | 5 +- .../impl/root/CreateRoomRootView.kt | 41 ++++++++++++++++- .../impl/root/CreateRoomRootPresenterTests.kt | 9 ++-- .../selectusers/api/SelectUsersEvents.kt | 1 + .../impl/DefaultSelectUsersPresenter.kt | 1 + .../libraries/matrix/api/MatrixClient.kt | 3 ++ .../libraries/matrix/impl/RustMatrixClient.kt | 27 +++++++++++ .../libraries/matrix/test/FakeMatrixClient.kt | 29 ++++++++++-- 17 files changed, 220 insertions(+), 28 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 4b2d653d79..fa2643b4ff 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -32,6 +32,7 @@ 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 com.bumble.appyx.navmodel.backstack.operation.replace import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode @@ -178,7 +179,16 @@ class LoggedInFlowNode @AssistedInject constructor( .build() } NavTarget.CreateRoom -> { - createRoomEntryPoint.createNode(this, buildContext) + val callback = object : CreateRoomEntryPoint.Callback { + override fun onOpenRoom(roomId: RoomId) { + backstack.replace(NavTarget.Room(roomId)) + } + } + + createRoomEntryPoint + .nodeBuilder(this, buildContext) + .callback(callback) + .build() } NavTarget.VerifySession -> { verifySessionEntryPoint.createNode(this, buildContext) diff --git a/features/createroom/api/build.gradle.kts b/features/createroom/api/build.gradle.kts index b3fceedc27..cafcf7c4b5 100644 --- a/features/createroom/api/build.gradle.kts +++ b/features/createroom/api/build.gradle.kts @@ -24,4 +24,5 @@ android { dependencies { implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) } diff --git a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt index 049c101806..73f5110daa 100644 --- a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt +++ b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt @@ -16,6 +16,21 @@ package io.element.android.features.createroom.api -import io.element.android.libraries.architecture.SimpleFeatureEntryPoint +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.matrix.api.core.RoomId -interface CreateRoomEntryPoint : SimpleFeatureEntryPoint +interface CreateRoomEntryPoint : FeatureEntryPoint { + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + fun onOpenRoom(roomId: RoomId) + } +} 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 13c3ff52be..22851904c4 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 @@ -23,17 +23,20 @@ import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.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.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.impl.addpeople.AddPeopleNode import io.element.android.features.createroom.impl.root.CreateRoomRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) @@ -64,6 +67,10 @@ class CreateRoomFlowNode @AssistedInject constructor( override fun onCreateNewRoom() { backstack.push(NavTarget.NewRoom) } + + override fun onOpenRoom(roomId: RoomId) { + plugins().forEach { it.onOpenRoom(roomId) } + } } createNode(buildContext, plugins = listOf(callback)) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt index 214ed3a9ec..34e514be3e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt @@ -18,6 +18,7 @@ package io.element.android.features.createroom.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.libraries.architecture.createNode @@ -26,7 +27,21 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultCreateRoomEntryPoint @Inject constructor() : CreateRoomEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode(buildContext) + + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreateRoomEntryPoint.NodeBuilder { + + val plugins = ArrayList() + + return object : CreateRoomEntryPoint.NodeBuilder { + + override fun callback(callback: CreateRoomEntryPoint.Callback): CreateRoomEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } } } 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 5d2f0f684c..d90708ed2b 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,6 +19,8 @@ package io.element.android.features.createroom.impl.root import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface CreateRoomRootEvents { - data class StartDM(val matrixUser: MatrixUser) : CreateRoomRootEvents object InvitePeople : CreateRoomRootEvents + data class SelectUser(val matrixUser: MatrixUser) : CreateRoomRootEvents + data class CreateDM(val matrixUser: MatrixUser) : CreateRoomRootEvents + object CancelCreateDM : 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 dadc16efc4..3a714a0eb3 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 @@ -16,7 +16,6 @@ package io.element.android.features.createroom.impl.root -import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext @@ -27,7 +26,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.SessionScope -import kotlinx.parcelize.Parcelize +import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(SessionScope::class) class CreateRoomRootNode @AssistedInject constructor( @@ -38,15 +37,17 @@ class CreateRoomRootNode @AssistedInject constructor( interface Callback : Plugin { fun onCreateNewRoom() + fun onOpenRoom(roomId: RoomId) } - private fun onCreateNewRoom() { - plugins().forEach { it.onCreateNewRoom() } - } + private val callback = object : Callback { + override fun onCreateNewRoom() { + plugins().forEach { it.onCreateNewRoom() } + } - sealed interface NavTarget : Parcelable { - @Parcelize - object Root : NavTarget + override fun onOpenRoom(roomId: RoomId) { + plugins().forEach { it.onOpenRoom(roomId) } + } } @Composable @@ -56,7 +57,8 @@ class CreateRoomRootNode @AssistedInject constructor( state = state, modifier = modifier, onClosePressed = this::navigateUp, - onNewRoomClicked = this::onCreateNewRoom, + onNewRoomClicked = callback::onCreateNewRoom, + onOpenDM = callback::onOpenRoom, ) } } 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 2f3f3ded4a..68901037f0 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,16 +17,30 @@ package io.element.android.features.createroom.impl.root import androidx.compose.runtime.Composable +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 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.Async import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.execute +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.ui.model.MatrixUser -import timber.log.Timber +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import javax.inject.Inject class CreateRoomRootPresenter @Inject constructor( private val presenterFactory: SelectUsersPresenter.Factory, + private val matrixClient: MatrixClient, ) : Presenter { private val presenter by lazy { @@ -37,20 +51,44 @@ class CreateRoomRootPresenter @Inject constructor( override fun present(): CreateRoomRootState { val selectUsersState = presenter.present() + val localCoroutineScope = rememberCoroutineScope() + + var showCreateDmConfirmationDialog by rememberSaveable { mutableStateOf(false) } + val startDmAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) } + fun handleEvents(event: CreateRoomRootEvents) { when (event) { - is CreateRoomRootEvents.StartDM -> handleStartDM(event.matrixUser) + is CreateRoomRootEvents.SelectUser -> { + val existingDM = matrixClient.findDM(event.matrixUser.id) + if (existingDM == null) { + showCreateDmConfirmationDialog = true + } else { + startDmAction.value = Async.Success(existingDM.roomId) + } + } + is CreateRoomRootEvents.CreateDM -> { + showCreateDmConfirmationDialog = false + localCoroutineScope.createDM(event.matrixUser, startDmAction) + } + CreateRoomRootEvents.CancelCreateDM -> { + showCreateDmConfirmationDialog = false + selectUsersState.eventSink(SelectUsersEvents.ClearSelection) + } CreateRoomRootEvents.InvitePeople -> Unit // Todo Handle invite people action } } return CreateRoomRootState( selectUsersState = selectUsersState, + showCreateDmConfirmationDialog = showCreateDmConfirmationDialog, + startDmAction = startDmAction.value, eventSink = ::handleEvents, ) } - private fun handleStartDM(matrixUser: MatrixUser) { - Timber.d("handleStartDM: $matrixUser") // Todo handle start DM action + private fun CoroutineScope.createDM(user: MatrixUser, startDmAction: MutableState>) = launch { + suspend { + matrixClient.createDM(user.id).getOrThrow() + }.execute(startDmAction) } } 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 a57d6aaaf6..89b702a011 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,12 @@ package io.element.android.features.createroom.impl.root import io.element.android.features.selectusers.api.SelectUsersState +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId data class CreateRoomRootState( val selectUsersState: SelectUsersState, + val showCreateDmConfirmationDialog: Boolean, + val startDmAction: Async, val eventSink: (CreateRoomRootEvents) -> Unit, ) 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 678f02476c..750b9cfe32 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 @@ -18,6 +18,7 @@ package io.element.android.features.createroom.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.selectusers.api.aSelectUsersState +import io.element.android.libraries.architecture.Async open class CreateRoomRootStateProvider : PreviewParameterProvider { override val values: Sequence @@ -28,5 +29,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider Unit = {}, onNewRoomClicked: () -> Unit = {}, + onOpenDM: (RoomId) -> Unit = {}, ) { + if (state.startDmAction is Async.Success) { + LaunchedEffect(state.startDmAction) { + onOpenDM(state.startDmAction.state) + } + } + Scaffold( modifier = modifier.fillMaxWidth(), topBar = { @@ -72,7 +85,7 @@ fun CreateRoomRootView( SelectUsersView( modifier = Modifier.fillMaxWidth(), state = state.selectUsersState, - onUserSelected = { state.eventSink.invoke(CreateRoomRootEvents.StartDM(it)) }, + onUserSelected = { state.eventSink(CreateRoomRootEvents.SelectUser(it)) }, ) if (!state.selectUsersState.isSearchActive) { @@ -83,6 +96,12 @@ fun CreateRoomRootView( } } } + + CreateDmConfirmationDialog(state) + + if (state.startDmAction is Async.Loading) { + ProgressDialog(text = "Creating room...") + } } @OptIn(ExperimentalMaterial3Api::class) @@ -153,6 +172,26 @@ fun CreateRoomActionButton( } } +@Composable +fun CreateDmConfirmationDialog( + state: CreateRoomRootState, + modifier: Modifier = Modifier, +) { + if (state.showCreateDmConfirmationDialog) { + val selectedUser = state.selectUsersState.selectedUsers.firstOrNull() + if (selectedUser != null) { + ConfirmationDialog( + modifier = modifier, + title = "Start chat", + content = "You're about starting a chat with ${selectedUser.getBestName()}, do you want to continue?", + submitText = stringResource(io.element.android.libraries.ui.strings.R.string._continue), + onSubmitClicked = { state.eventSink(CreateRoomRootEvents.CreateDM(selectedUser)) }, + onDismiss = { state.eventSink(CreateRoomRootEvents.CancelCreateDM) }, + ) + } + } +} + @Preview @Composable fun CreateRoomRootViewLightPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) = 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 5dc9ec00e9..4aa1068ce4 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 @@ -25,6 +25,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.selectusers.api.SelectUsersPresenterArgs import io.element.android.features.selectusers.impl.DefaultSelectUsersPresenter import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -34,13 +35,15 @@ import org.junit.Test class CreateRoomRootPresenterTests { private lateinit var presenter: CreateRoomRootPresenter + private lateinit var fakeMatrixClient: FakeMatrixClient @Before fun setup() { val selectUsersPresenter = object : DefaultSelectUsersPresenter.DefaultSelectUsersFactory { override fun create(args: SelectUsersPresenterArgs) = DefaultSelectUsersPresenter(args) } - presenter = CreateRoomRootPresenter(selectUsersPresenter) + fakeMatrixClient = FakeMatrixClient() + presenter = CreateRoomRootPresenter(selectUsersPresenter, fakeMatrixClient) } @Test @@ -64,13 +67,13 @@ class CreateRoomRootPresenterTests { } @Test - fun `present - trigger start DM action`() = runTest { + fun `present - trigger select user action`() = runTest { moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() val matrixUser = MatrixUser(UserId("@name:matrix.org")) - initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser)) + initialState.eventSink(CreateRoomRootEvents.SelectUser(matrixUser)) } } } 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 e0ee6ddf68..22c72f6dfb 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,5 +22,6 @@ sealed interface SelectUsersEvents { data class UpdateSearchQuery(val query: String) : SelectUsersEvents data class AddToSelection(val matrixUser: MatrixUser) : SelectUsersEvents data class RemoveFromSelection(val matrixUser: MatrixUser) : SelectUsersEvents + object ClearSelection : SelectUsersEvents data class OnSearchActiveChanged(val active: Boolean) : SelectUsersEvents } 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 e1135cd1a2..55c2cd694d 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 @@ -79,6 +79,7 @@ class DefaultSelectUsersPresenter @AssistedInject constructor( localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) } is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + SelectUsersEvents.ClearSelection -> selectedUsers.value = persistentListOf() } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 9ce5502843..15ba838bcb 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource @@ -28,6 +29,8 @@ interface MatrixClient : Closeable { val sessionId: SessionId val roomSummaryDataSource: RoomSummaryDataSource fun getRoom(roomId: RoomId): MatrixRoom? + suspend fun createDM(userId: UserId): Result + fun findDM(userId: UserId): MatrixRoom? fun startSync() fun stopSync() fun mediaResolver(): MediaResolver diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 378fec56c7..ce9286d2be 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -37,7 +37,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate +import org.matrix.rustcomponents.sdk.CreateRoomParameters import org.matrix.rustcomponents.sdk.RequiredState +import org.matrix.rustcomponents.sdk.RoomPreset +import org.matrix.rustcomponents.sdk.RoomVisibility import org.matrix.rustcomponents.sdk.SlidingSyncListBuilder import org.matrix.rustcomponents.sdk.SlidingSyncMode import org.matrix.rustcomponents.sdk.SlidingSyncRequestListFilters @@ -154,6 +157,30 @@ class RustMatrixClient constructor( ) } + override fun findDM(userId: UserId): MatrixRoom? { + val roomId = client.getDmRoom(userId.value)?.use { RoomId(it.id()) } + return roomId?.let { getRoom(it) } + } + + override suspend fun createDM(userId: UserId): Result = + withContext(dispatchers.io) { + runCatching { + val roomId = client.createRoom( + CreateRoomParameters( + name = "", + topic = null, + isEncrypted = true, + isDirect = true, + visibility = RoomVisibility.PRIVATE, + preset = RoomPreset.TRUSTED_PRIVATE_CHAT, + invite = listOf(userId.value), + avatar = null, + ) + ) + RoomId(roomId) + } + } + override fun mediaResolver(): MediaResolver = mediaResolver override fun sessionVerificationService(): SessionVerificationService = verificationService diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 9272e794b4..ec5068e93d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.test import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaResolver import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource @@ -37,12 +38,22 @@ class FakeMatrixClient( private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService() ) : MatrixClient { + private var createDmResult: Result = Result.success(A_ROOM_ID) + private var findDmResult: MatrixRoom? = FakeMatrixRoom() private var logoutFailure: Throwable? = null override fun getRoom(roomId: RoomId): MatrixRoom? { return FakeMatrixRoom(roomId) } + override suspend fun createDM(userId: UserId): Result { + return createDmResult + } + + override fun findDM(userId: UserId): MatrixRoom? { + return findDmResult + } + override fun startSync() = Unit override fun stopSync() = Unit @@ -51,10 +62,6 @@ class FakeMatrixClient( return FakeMediaResolver() } - fun givenLogoutError(failure: Throwable) { - logoutFailure = failure - } - override suspend fun logout() { delay(100) logoutFailure?.let { throw it } @@ -81,4 +88,18 @@ class FakeMatrixClient( override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService override fun onSlidingSyncUpdate() {} + + // Mocks + + fun givenLogoutError(failure: Throwable) { + logoutFailure = failure + } + + fun givenCreateDmResult(result: Result) { + createDmResult = result + } + + fun givenFindDmResult(result: MatrixRoom?) { + findDmResult = result + } }