Browse Source

Merge pull request #324 from vector-im/feature/fre/create_room

Handle create room action
test/jme/compound-poc
Florian Renaud 1 year ago committed by GitHub
parent
commit
13f1ca3a60
  1. 2
      appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
  2. 1
      changelog.d/111.feature
  3. 2
      features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt
  4. 4
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt
  5. 12
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt
  6. 4
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt
  7. 15
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt
  8. 43
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt
  9. 3
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt
  10. 2
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt
  11. 29
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
  12. 8
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt
  13. 1
      features/createroom/impl/src/main/res/values/localazy.xml
  14. 60
      features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
  15. 72
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  16. 7
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
  17. 45
      libraries/ui-strings/src/main/res/values/localazy.xml

2
appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt

@ -199,7 +199,7 @@ class LoggedInFlowNode @AssistedInject constructor( @@ -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))
}
}

1
changelog.d/111.feature

@ -0,0 +1 @@ @@ -0,0 +1 @@
[Create and join rooms] Create a room and show it

2
features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt

@ -31,6 +31,6 @@ interface CreateRoomEntryPoint : FeatureEntryPoint { @@ -31,6 +31,6 @@ interface CreateRoomEntryPoint : FeatureEntryPoint {
}
interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId)
fun onSuccess(roomId: RoomId)
}
}

4
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/ConfigureRoomFlowNode.kt

@ -76,10 +76,10 @@ class ConfigureRoomFlowNode @AssistedInject constructor( @@ -76,10 +76,10 @@ class ConfigureRoomFlowNode @AssistedInject constructor(
backstack.push(NavTarget.ConfigureRoom)
}
}
createNode<AddPeopleNode>(context = buildContext, plugins = listOf(callback))
createNode<AddPeopleNode>(context = buildContext, plugins = plugins.plus(callback))
}
NavTarget.ConfigureRoom -> {
createNode<ConfigureRoomNode>(context = buildContext)
createNode<ConfigureRoomNode>(context = buildContext, plugins = plugins)
}
}
}

12
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt

@ -30,6 +30,7 @@ import dagger.assisted.Assisted @@ -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( @@ -67,14 +68,19 @@ class CreateRoomFlowNode @AssistedInject constructor(
backstack.push(NavTarget.NewRoom)
}
override fun onOpenRoom(roomId: RoomId) {
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) }
override fun onStartChatSuccess(roomId: RoomId) {
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onSuccess(roomId) }
}
}
createNode<CreateRoomRootNode>(context = buildContext, plugins = listOf(callback))
}
NavTarget.NewRoom -> {
createNode<ConfigureRoomFlowNode>(context = buildContext, plugins = emptyList())
val callback = object : ConfigureRoomNode.Callback {
override fun onCreateRoomSuccess(roomId: RoomId) {
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onSuccess(roomId) }
}
}
createNode<ConfigureRoomFlowNode>(context = buildContext, plugins = listOf(callback))
}
}
}

4
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt

@ -17,6 +17,7 @@ @@ -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 { @@ -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
}

15
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt

@ -21,10 +21,12 @@ import androidx.compose.ui.Modifier @@ -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( @@ -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<Callback>().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
)
}
}

43
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt

@ -17,17 +17,30 @@ @@ -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<ConfigureRoomState> {
@Composable
@ -39,6 +52,14 @@ class ConfigureRoomPresenter @Inject constructor( @@ -39,6 +52,14 @@ class ConfigureRoomPresenter @Inject constructor(
}
}
val localCoroutineScope = rememberCoroutineScope()
val createRoomAction: MutableState<Async<RoomId>> = 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( @@ -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<Async<RoomId>>) = 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)
}
}

3
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt

@ -17,9 +17,12 @@ @@ -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<RoomId>,
val eventSink: (ConfigureRoomEvents) -> Unit
)

2
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 @@ -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<ConfigureRoomState> {
override val values: Sequence<ConfigureRoomState>
@ -39,5 +40,6 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt @@ -39,5 +40,6 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
fun aConfigureRoomState() = ConfigureRoomState(
config = CreateRoomConfig(),
isCreateButtonEnabled = false,
createRoomAction = Async.Uninitialized,
eventSink = {}
)

29
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt

@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.padding @@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -44,13 +45,17 @@ import io.element.android.features.createroom.impl.components.Avatar @@ -44,13 +45,17 @@ import io.element.android.features.createroom.impl.components.Avatar
import io.element.android.features.createroom.impl.components.LabelledTextField
import io.element.android.features.createroom.impl.components.RoomPrivacyOption
import io.element.android.features.userlist.api.components.SelectedUsersList
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@ -58,7 +63,14 @@ fun ConfigureRoomView( @@ -58,7 +63,14 @@ fun ConfigureRoomView(
state: ConfigureRoomState,
modifier: Modifier = Modifier,
onBackPressed: () -> 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( @@ -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( @@ -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

8
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootNode.kt

@ -37,7 +37,7 @@ class CreateRoomRootNode @AssistedInject constructor( @@ -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( @@ -45,8 +45,8 @@ class CreateRoomRootNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onCreateNewRoom() }
}
override fun onOpenRoom(roomId: RoomId) {
plugins<Callback>().forEach { it.onOpenRoom(roomId) }
override fun onStartChatSuccess(roomId: RoomId) {
plugins<Callback>().forEach { it.onStartChatSuccess(roomId) }
}
}
@ -58,7 +58,7 @@ class CreateRoomRootNode @AssistedInject constructor( @@ -58,7 +58,7 @@ class CreateRoomRootNode @AssistedInject constructor(
modifier = modifier,
onClosePressed = this::navigateUp,
onNewRoomClicked = callback::onCreateNewRoom,
onOpenDM = callback::onOpenRoom,
onOpenDM = callback::onStartChatSuccess,
)
}
}

1
features/createroom/impl/src/main/res/values/localazy.xml

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
<string name="screen_create_room_action_create_room">"New room"</string>
<string name="screen_create_room_action_invite_people">"Invite people"</string>
<string name="screen_create_room_add_people_title">"Add people"</string>
<string name="screen_create_room_error_creating_room">"An error occurred when creating the room"</string>
<string name="screen_create_room_private_option_description">"Messages in this room are encrypted. Encryption can’t be disabled afterwards."</string>
<string name="screen_create_room_private_option_title">"Private room (invite only)"</string>
<string name="screen_create_room_public_option_description">"Messages are not encrypted and anyone can read them. You can enable encryption at a later date."</string>

60
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 @@ -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 { @@ -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 { @@ -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<RoomId>(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)
}
}
}

72
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 @@ -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 @@ -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( @@ -180,43 +184,44 @@ class RustMatrixClient constructor(
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> = 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<RoomId> = 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<RoomId> {
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( @@ -321,3 +326,4 @@ class RustMatrixClient constructor(
return sessionDirectory.deleteRecursively()
}
}

7
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt

@ -46,6 +46,7 @@ class FakeMatrixClient( @@ -46,6 +46,7 @@ class FakeMatrixClient(
private val notificationService: FakeNotificationService = FakeNotificationService(),
) : MatrixClient {
private var createRoomResult: Result<RoomId> = Result.success(A_ROOM_ID)
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
private var createDmFailure: Throwable? = null
private var findDmResult: MatrixRoom? = FakeMatrixRoom()
@ -61,7 +62,7 @@ class FakeMatrixClient( @@ -61,7 +62,7 @@ class FakeMatrixClient(
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> {
delay(100)
return Result.success(A_ROOM_ID)
return createRoomResult
}
override suspend fun createDM(userId: UserId): Result<RoomId> {
@ -119,6 +120,10 @@ class FakeMatrixClient( @@ -119,6 +120,10 @@ class FakeMatrixClient(
logoutFailure = failure
}
fun givenCreateRoomResult(result: Result<RoomId>) {
createRoomResult = result
}
fun givenCreateDmResult(result: Result<RoomId>) {
createDmResult = result
}

45
libraries/ui-strings/src/main/res/values/localazy.xml

@ -121,60 +121,15 @@ @@ -121,60 +121,15 @@
<string name="leave_room_alert_private_subtitle">"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."</string>
<string name="leave_room_alert_subtitle">"Are you sure that you want to leave the room?"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="notification_channel_call">"Call"</string>
<string name="notification_channel_listening_for_events">"Listening for events"</string>
<string name="notification_channel_noisy">"Noisy notifications"</string>
<string name="notification_channel_silent">"Silent notifications"</string>
<string name="notification_inline_reply_failed">"** Failed to send - please open room"</string>
<string name="notification_invitation_action_join">"Join"</string>
<string name="notification_invitation_action_reject">"Reject"</string>
<string name="notification_new_messages">"New Messages"</string>
<string name="notification_room_action_mark_as_read">"Mark as read"</string>
<string name="notification_room_action_quick_reply">"Quick reply"</string>
<string name="notification_sender_me">"Me"</string>
<string name="notification_test_push_notification_content">"You are viewing the notification! Click me!"</string>
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
<string name="notification_unread_notified_messages_and_invitation">"%1$s and %2$s"</string>
<string name="notification_unread_notified_messages_in_room">"%1$s in %2$s"</string>
<string name="notification_unread_notified_messages_in_room_and_invitation">"%1$s in %2$s and %3$s"</string>
<plurals name="common_member_count">
<item quantity="one">"%1$d member"</item>
<item quantity="other">"%1$d members"</item>
</plurals>
<plurals name="notification_compat_summary_line_for_room">
<item quantity="one">"%1$s: %2$d message"</item>
<item quantity="other">"%1$s: %2$d messages"</item>
</plurals>
<plurals name="notification_compat_summary_title">
<item quantity="one">"%d notification"</item>
<item quantity="other">"%d notifications"</item>
</plurals>
<plurals name="notification_invitations">
<item quantity="one">"%d invitation"</item>
<item quantity="other">"%d invitations"</item>
</plurals>
<plurals name="notification_new_messages_for_room">
<item quantity="one">"%d new message"</item>
<item quantity="other">"%d new messages"</item>
</plurals>
<plurals name="notification_unread_notified_messages">
<item quantity="one">"%d unread notified message"</item>
<item quantity="other">"%d unread notified messages"</item>
</plurals>
<plurals name="notification_unread_notified_messages_in_room_rooms">
<item quantity="one">"%d room"</item>
<item quantity="other">"%d rooms"</item>
</plurals>
<plurals name="room_timeline_state_changes">
<item quantity="one">"%1$d room change"</item>
<item quantity="other">"%1$d room changes"</item>
</plurals>
<string name="preference_rageshake">"Rageshake to report bug"</string>
<string name="push_choose_distributor_dialog_title_android">"Choose how to receive notifications"</string>
<string name="push_distributor_background_sync_android">"Background synchronization"</string>
<string name="push_distributor_firebase_android">"Google Services"</string>
<string name="push_no_valid_google_play_services_apk_android">"No valid Google Play Services found. Notifications may not work properly."</string>
<string name="rageshake_dialog_content">"You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"</string>
<string name="report_content_explanation">"This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."</string>
<string name="report_content_hint">"Reason for reporting this content"</string>

Loading…
Cancel
Save