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 f3eac99a02..95f883e39f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -199,7 +199,7 @@ class LoggedInFlowNode @AssistedInject constructor( } NavTarget.CreateRoom -> { val callback = object : CreateRoomEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId) { + override fun onSuccess(roomId: RoomId) { backstack.replace(NavTarget.Room(roomId)) } } diff --git a/changelog.d/111.feature b/changelog.d/111.feature new file mode 100644 index 0000000000..865bb39cbd --- /dev/null +++ b/changelog.d/111.feature @@ -0,0 +1 @@ +[Create and join rooms] Create a room and show it 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 73f5110daa..18e0e4e28f 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 @@ -31,6 +31,6 @@ interface CreateRoomEntryPoint : FeatureEntryPoint { } interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId) + fun onSuccess(roomId: RoomId) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt index a01f0747f5..2575c8b66f 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt @@ -76,10 +76,10 @@ class ConfigureRoomFlowNode @AssistedInject constructor( backstack.push(NavTarget.ConfigureRoom) } } - createNode(context = buildContext, plugins = listOf(callback)) + createNode(context = buildContext, plugins = plugins.plus(callback)) } NavTarget.ConfigureRoom -> { - createNode(context = buildContext) + createNode(context = buildContext, plugins = plugins) } } } 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 6b9edc68d2..6f447e6bc9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -30,6 +30,7 @@ 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.configureroom.ConfigureRoomNode import io.element.android.features.createroom.impl.root.CreateRoomRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -67,14 +68,19 @@ class CreateRoomFlowNode @AssistedInject constructor( backstack.push(NavTarget.NewRoom) } - override fun onOpenRoom(roomId: RoomId) { - plugins().forEach { it.onOpenRoom(roomId) } + override fun onStartChatSuccess(roomId: RoomId) { + plugins().forEach { it.onSuccess(roomId) } } } createNode(context = buildContext, plugins = listOf(callback)) } NavTarget.NewRoom -> { - createNode(context = buildContext, plugins = emptyList()) + val callback = object : ConfigureRoomNode.Callback { + override fun onCreateRoomSuccess(roomId: RoomId) { + plugins().forEach { it.onSuccess(roomId) } + } + } + createNode(context = buildContext, plugins = listOf(callback)) } } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index f10f673d78..c444e83c16 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -17,6 +17,7 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri +import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.libraries.matrix.ui.model.MatrixUser sealed interface ConfigureRoomEvents { @@ -25,5 +26,6 @@ sealed interface ConfigureRoomEvents { data class AvatarUriChanged(val uri: Uri?) : ConfigureRoomEvents data class RoomPrivacyChanged(val privacy: RoomPrivacy?) : ConfigureRoomEvents data class RemoveFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents - object CreateRoom : ConfigureRoomEvents + data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents + object CancelCreateRoom : ConfigureRoomEvents } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index 08a4f5f64b..060bb447e7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -21,10 +21,12 @@ 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.createroom.impl.di.CreateRoomScope +import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(CreateRoomScope::class) class ConfigureRoomNode @AssistedInject constructor( @@ -33,13 +35,24 @@ class ConfigureRoomNode @AssistedInject constructor( private val presenter: ConfigureRoomPresenter, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onCreateRoomSuccess(roomId: RoomId) + } + + private val callback = object : Callback { + override fun onCreateRoomSuccess(roomId: RoomId) { + plugins().forEach { it.onCreateRoomSuccess(roomId) } + } + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() ConfigureRoomView( state = state, modifier = modifier, - onBackPressed = { navigateUp() } // TODO we should keep in memory the current view state + onBackPressed = this::navigateUp, + onRoomCreated = callback::onCreateRoomSuccess ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 0046b83058..99d093a4ee 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -17,17 +17,30 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.runtime.Composable +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 io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore +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.api.createroom.CreateRoomParameters +import io.element.android.libraries.matrix.api.createroom.RoomPreset +import io.element.android.libraries.matrix.api.createroom.RoomVisibility +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( private val dataStore: CreateRoomDataStore, + private val matrixClient: MatrixClient, ) : Presenter { @Composable @@ -39,6 +52,14 @@ class ConfigureRoomPresenter @Inject constructor( } } + val localCoroutineScope = rememberCoroutineScope() + val createRoomAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) } + + fun createRoom(config: CreateRoomConfig) { + createRoomAction.value = Async.Uninitialized + localCoroutineScope.createRoom(config, createRoomAction) + } + fun handleEvents(event: ConfigureRoomEvents) { when (event) { is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setAvatarUrl(event.uri?.toString()) @@ -46,14 +67,32 @@ class ConfigureRoomPresenter @Inject constructor( is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) - ConfigureRoomEvents.CreateRoom -> Unit // TODO + is ConfigureRoomEvents.CreateRoom -> createRoom(event.config) + ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = Async.Uninitialized } } return ConfigureRoomState( - createRoomConfig.value, + config = createRoomConfig.value, isCreateButtonEnabled = isCreateButtonEnabled, + createRoomAction = createRoomAction.value, eventSink = ::handleEvents, ) } + + private fun CoroutineScope.createRoom(config: CreateRoomConfig, createRoomAction: MutableState>) = launch { + suspend { + val params = CreateRoomParameters( + name = config.roomName, + topic = config.topic, + isEncrypted = config.privacy == RoomPrivacy.Private, + isDirect = false, + visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE, + preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, + invite = config.invites.map { it.id }, + avatar = config.avatarUrl, + ) + matrixClient.createRoom(params).getOrThrow() + }.execute(createRoomAction) + } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index 47be0b1f17..b5f804fcde 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -17,9 +17,12 @@ package io.element.android.features.createroom.impl.configureroom import io.element.android.features.createroom.impl.CreateRoomConfig +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId data class ConfigureRoomState( val config: CreateRoomConfig, val isCreateButtonEnabled: Boolean, + val createRoomAction: Async, val eventSink: (ConfigureRoomEvents) -> Unit ) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt index 32744178dd..67e0b91bba 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.userlist.api.aListOfSelectedUsers +import io.element.android.libraries.architecture.Async open class ConfigureRoomStateProvider : PreviewParameterProvider { override val values: Sequence @@ -39,5 +40,6 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider Unit = {}, + onRoomCreated: (RoomId) -> Unit = {}, ) { + if (state.createRoomAction is Async.Success) { + LaunchedEffect(state.createRoomAction) { + onRoomCreated(state.createRoomAction.state) + } + } + val context = LocalContext.current Scaffold( modifier = modifier, @@ -67,8 +79,7 @@ fun ConfigureRoomView( isNextActionEnabled = state.isCreateButtonEnabled, onBackPressed = onBackPressed, onNextPressed = { - // state.eventSink(ConfigureRoomEvents.CreateRoom) - Toast.makeText(context, "not implemented yet", Toast.LENGTH_SHORT).show() + state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) }, ) } @@ -102,6 +113,20 @@ fun ConfigureRoomView( ) } } + + when (state.createRoomAction) { + is Async.Loading -> { + ProgressDialog(text = stringResource(StringR.string.common_creating_room)) + } + is Async.Failure -> { + RetryDialog( + content = stringResource(R.string.screen_create_room_error_creating_room), + onDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) }, + onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) }, + ) + } + else -> Unit + } } @Composable 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 3a714a0eb3..596255dc15 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 @@ -37,7 +37,7 @@ class CreateRoomRootNode @AssistedInject constructor( interface Callback : Plugin { fun onCreateNewRoom() - fun onOpenRoom(roomId: RoomId) + fun onStartChatSuccess(roomId: RoomId) } private val callback = object : Callback { @@ -45,8 +45,8 @@ class CreateRoomRootNode @AssistedInject constructor( plugins().forEach { it.onCreateNewRoom() } } - override fun onOpenRoom(roomId: RoomId) { - plugins().forEach { it.onOpenRoom(roomId) } + override fun onStartChatSuccess(roomId: RoomId) { + plugins().forEach { it.onStartChatSuccess(roomId) } } } @@ -58,7 +58,7 @@ class CreateRoomRootNode @AssistedInject constructor( modifier = modifier, onClosePressed = this::navigateUp, onNewRoomClicked = callback::onCreateNewRoom, - onOpenDM = callback::onOpenRoom, + onOpenDM = callback::onStartChatSuccess, ) } } diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index 177d588f7e..5d78791ee4 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -3,6 +3,7 @@ "New room" "Invite people" "Add people" + "An error occurred when creating the room" "Messages in this room are encrypted. Encryption can’t be disabled afterwards." "Private room (invite only)" "Messages are not encrypted and anyone can read them. You can enable encryption at a later date." diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 1f27baa511..3f467b1d40 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -26,9 +26,13 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.UserListDataStore +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.ui.components.aMatrixUser import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -44,11 +48,16 @@ class ConfigureRoomPresenterTests { private lateinit var presenter: ConfigureRoomPresenter private lateinit var userListDataStore: UserListDataStore + private lateinit var fakeMatrixClient: FakeMatrixClient @Before fun setup() { + fakeMatrixClient = FakeMatrixClient() userListDataStore = UserListDataStore() - presenter = ConfigureRoomPresenter(CreateRoomDataStore(userListDataStore)) + presenter = ConfigureRoomPresenter( + dataStore = CreateRoomDataStore(userListDataStore), + matrixClient = fakeMatrixClient + ) } @Test @@ -149,5 +158,54 @@ class ConfigureRoomPresenterTests { assertThat(newState.config).isEqualTo(expectedConfig) } } + + @Test + fun `present - trigger create room action`() = runTest { + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val createRoomResult = Result.success(RoomId("!createRoomResult")) + + fakeMatrixClient.givenCreateRoomResult(createRoomResult) + + initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java) + val stateAfterCreateRoom = awaitItem() + assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(Async.Success::class.java) + assertThat(stateAfterCreateRoom.createRoomAction.dataOrNull()).isEqualTo(createRoomResult.getOrNull()) + } + } + + @Test + fun `present - trigger retry and cancel actions`() = runTest { + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val createRoomResult = Result.failure(A_THROWABLE) + + fakeMatrixClient.givenCreateRoomResult(createRoomResult) + + // Create + initialState.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java) + val stateAfterCreateRoom = awaitItem() + assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(Async.Failure::class.java) + assertThat((stateAfterCreateRoom.createRoomAction as? Async.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) + + // Retry + stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom(initialState.config)) + assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Uninitialized::class.java) + assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Loading::class.java) + val stateAfterRetry = awaitItem() + assertThat(stateAfterRetry.createRoomAction).isInstanceOf(Async.Failure::class.java) + assertThat((stateAfterRetry.createRoomAction as? Async.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull()) + + // Cancel + stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom) + assertThat(awaitItem().createRoomAction).isInstanceOf(Async.Uninitialized::class.java) + } + } } 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 fe3dbfb911..7408bd12f1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.asRoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility @@ -40,9 +41,12 @@ import io.element.android.libraries.matrix.impl.verification.RustSessionVerifica import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.RequiredState @@ -180,43 +184,44 @@ class RustMatrixClient constructor( override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result = withContext(dispatchers.io) { runCatching { - val roomId = client.createRoom( - RustCreateRoomParameters( - name = createRoomParams.name, - topic = createRoomParams.topic, - isEncrypted = createRoomParams.isEncrypted, - isDirect = createRoomParams.isDirect, - visibility = when (createRoomParams.visibility) { - RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC - RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE - }, - preset = when (createRoomParams.preset) { - RoomPreset.PRIVATE_CHAT -> RustRoomPreset.PRIVATE_CHAT - RoomPreset.PUBLIC_CHAT -> RustRoomPreset.PUBLIC_CHAT - RoomPreset.TRUSTED_PRIVATE_CHAT -> RustRoomPreset.TRUSTED_PRIVATE_CHAT - }, - invite = createRoomParams.invite?.map { it.value }, - avatar = createRoomParams.avatar, - ) + val rustParams = RustCreateRoomParameters( + name = createRoomParams.name, + topic = createRoomParams.topic, + isEncrypted = createRoomParams.isEncrypted, + isDirect = createRoomParams.isDirect, + visibility = when (createRoomParams.visibility) { + RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC + RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE + }, + preset = when (createRoomParams.preset) { + RoomPreset.PRIVATE_CHAT -> RustRoomPreset.PRIVATE_CHAT + RoomPreset.PUBLIC_CHAT -> RustRoomPreset.PUBLIC_CHAT + RoomPreset.TRUSTED_PRIVATE_CHAT -> RustRoomPreset.TRUSTED_PRIVATE_CHAT + }, + invite = createRoomParams.invite?.map { it.value }, + avatar = createRoomParams.avatar, ) - RoomId(roomId) + val roomId = client.createRoom(rustParams).asRoomId() + + // Wait to receive the room back from the sync + withTimeout(30_000L) { + slidingSyncObserverProxy.updateSummaryFlow.filter { roomId.value in it.rooms }.first() + } + + roomId } } - override suspend fun createDM(userId: UserId): Result = withContext(dispatchers.io) { - runCatching { - val roomId = client.createRoom( - RustCreateRoomParameters( - name = null, - isEncrypted = true, - isDirect = true, - visibility = RustRoomVisibility.PRIVATE, - preset = RustRoomPreset.TRUSTED_PRIVATE_CHAT, - invite = listOf(userId.value), - ) - ) - RoomId(roomId) - } + override suspend fun createDM(userId: UserId): Result { + val createRoomParams = CreateRoomParameters( + name = null, + isEncrypted = true, + isDirect = true, + visibility = RoomVisibility.PRIVATE, + preset = RoomPreset.TRUSTED_PRIVATE_CHAT, + invite = listOf(userId) + ) + return createRoom(createRoomParams) } override fun mediaResolver(): MediaResolver = mediaResolver @@ -321,3 +326,4 @@ class RustMatrixClient constructor( return sessionDirectory.deleteRecursively() } } + 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 5cb79564ba..301869ce06 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 @@ -46,6 +46,7 @@ class FakeMatrixClient( private val notificationService: FakeNotificationService = FakeNotificationService(), ) : MatrixClient { + private var createRoomResult: Result = Result.success(A_ROOM_ID) private var createDmResult: Result = Result.success(A_ROOM_ID) private var createDmFailure: Throwable? = null private var findDmResult: MatrixRoom? = FakeMatrixRoom() @@ -61,7 +62,7 @@ class FakeMatrixClient( override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result { delay(100) - return Result.success(A_ROOM_ID) + return createRoomResult } override suspend fun createDM(userId: UserId): Result { @@ -119,6 +120,10 @@ class FakeMatrixClient( logoutFailure = failure } + fun givenCreateRoomResult(result: Result) { + createRoomResult = result + } + fun givenCreateDmResult(result: Result) { createDmResult = result } diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 72c7dfe08f..379dd5508f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -121,60 +121,15 @@ "Are you sure that you want to leave this room? This room is not public and you will not be able to rejoin without an invite." "Are you sure that you want to leave the room?" "%1$s Android" - "Call" - "Listening for events" - "Noisy notifications" - "Silent notifications" - "** Failed to send - please open room" - "Join" - "Reject" - "New Messages" - "Mark as read" - "Quick reply" - "Me" - "You are viewing the notification! Click me!" - "%1$s: %2$s" - "%1$s: %2$s %3$s" - "%1$s and %2$s" - "%1$s in %2$s" - "%1$s in %2$s and %3$s" "%1$d member" "%1$d members" - - "%1$s: %2$d message" - "%1$s: %2$d messages" - - - "%d notification" - "%d notifications" - - - "%d invitation" - "%d invitations" - - - "%d new message" - "%d new messages" - - - "%d unread notified message" - "%d unread notified messages" - - - "%d room" - "%d rooms" - "%1$d room change" "%1$d room changes" "Rageshake to report bug" - "Choose how to receive notifications" - "Background synchronization" - "Google Services" - "No valid Google Play Services found. Notifications may not work properly." "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?" "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." "Reason for reporting this content"