ConfigureRoomEvents -> ConfigureRoomEvent

This commit is contained in:
Benoit Marty
2026-08-25 09:51:17 +02:00
parent 7353e0e476
commit dff9af9ce0
6 changed files with 65 additions and 65 deletions
@@ -11,13 +11,13 @@ package io.element.android.features.createroom.impl.configureroom
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
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 JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvents
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents
data object CreateRoom : ConfigureRoomEvents
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents
data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvents
data object CancelCreateRoom : ConfigureRoomEvents
sealed interface ConfigureRoomEvent {
data class RoomNameChanged(val name: String) : ConfigureRoomEvent
data class TopicChanged(val topic: String) : ConfigureRoomEvent
data class JoinRuleChanged(val joinRuleItem: JoinRuleItem) : ConfigureRoomEvent
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvent
data object CreateRoom : ConfigureRoomEvent
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvent
data class SetParentSpace(val space: SpaceRoom?) : ConfigureRoomEvent
data object CancelCreateRoom : ConfigureRoomEvent
}
@@ -188,14 +188,14 @@ class ConfigureRoomPresenter(
localCoroutineScope.createRoom(config, createRoomAction)
}
fun handleEvent(event: ConfigureRoomEvents) {
fun handleEvent(event: ConfigureRoomEvent) {
when (event) {
is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name)
is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic)
is ConfigureRoomEvents.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem)
is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig)
is ConfigureRoomEvents.HandleAvatarAction -> {
is ConfigureRoomEvent.RoomNameChanged -> dataStore.setRoomName(event.name)
is ConfigureRoomEvent.TopicChanged -> dataStore.setTopic(event.topic)
is ConfigureRoomEvent.JoinRuleChanged -> dataStore.setJoinRule(event.joinRuleItem)
is ConfigureRoomEvent.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
is ConfigureRoomEvent.CreateRoom -> createRoom(createRoomConfig)
is ConfigureRoomEvent.HandleAvatarAction -> {
when (event.action) {
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
@@ -207,10 +207,10 @@ class ConfigureRoomPresenter(
AvatarAction.Remove -> dataStore.setAvatarUri(uri = null)
}
}
is ConfigureRoomEvents.SetParentSpace -> {
is ConfigureRoomEvent.SetParentSpace -> {
dataStore.setParentSpace(event.space, false)
}
ConfigureRoomEvents.CancelCreateRoom -> {
ConfigureRoomEvent.CancelCreateRoom -> {
createRoomAction.value = AsyncAction.Uninitialized
}
}
@@ -26,7 +26,7 @@ data class ConfigureRoomState(
val homeserverName: String,
val availableJoinRules: ImmutableList<JoinRuleItem>,
val spaces: ImmutableList<SpaceRoom>,
val eventSink: (ConfigureRoomEvents) -> Unit
val eventSink: (ConfigureRoomEvent) -> Unit
) {
val isValid: Boolean = config.roomName?.isNotEmpty() == true &&
(config.visibilityState is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid) &&
@@ -143,7 +143,7 @@ fun aConfigureRoomState(
)
},
spaces: List<SpaceRoom> = emptyList(),
eventSink: (ConfigureRoomEvents) -> Unit = { },
eventSink: (ConfigureRoomEvent) -> Unit = { },
) = ConfigureRoomState(
config = config,
isSpace = isSpace,
@@ -96,7 +96,7 @@ fun ConfigureRoomView(
onBackClick = onBackClick,
onNextClick = {
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.CreateRoom)
state.eventSink(ConfigureRoomEvent.CreateRoom)
},
)
}
@@ -114,20 +114,20 @@ fun ConfigureRoomView(
avatarUri = state.config.avatarUri,
roomName = state.config.roomName.orEmpty(),
onAvatarClick = ::onAvatarClick,
onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) },
onChangeRoomName = { state.eventSink(ConfigureRoomEvent.RoomNameChanged(it)) },
)
Spacer(modifier = Modifier.height(16.dp))
RoomTopic(
modifier = Modifier.padding(horizontal = 16.dp),
topic = state.config.topic.orEmpty(),
onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) },
onTopicChange = { state.eventSink(ConfigureRoomEvent.TopicChanged(it)) },
)
Spacer(modifier = Modifier.height(16.dp))
if (!state.isSpace && state.spaces.isNotEmpty()) {
SelectParentSpaceOptions(
spaces = state.spaces,
selectedSpace = state.config.parentSpace,
onSelectSpace = { state.eventSink(ConfigureRoomEvents.SetParentSpace(it)) },
onSelectSpace = { state.eventSink(ConfigureRoomEvent.SetParentSpace(it)) },
)
}
RoomJoinRuleOptions(
@@ -136,7 +136,7 @@ fun ConfigureRoomView(
parentSpace = state.config.parentSpace,
onOptionClick = {
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.JoinRuleChanged(it))
state.eventSink(ConfigureRoomEvent.JoinRuleChanged(it))
},
)
if (state.config.visibilityState !is RoomVisibilityState.Private) {
@@ -146,7 +146,7 @@ fun ConfigureRoomView(
address = state.config.visibilityState.roomAddress().getOrNull().orEmpty(),
homeserverName = state.homeserverName,
addressValidity = state.roomAddressValidity,
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
onAddressChange = { state.eventSink(ConfigureRoomEvent.RoomAddressChanged(it)) },
label = null,
supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
)
@@ -158,7 +158,7 @@ fun ConfigureRoomView(
actions = state.avatarActions,
isVisible = isAvatarActionsSheetVisible.value,
onDismiss = { isAvatarActionsSheetVisible.value = false },
onSelectAction = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) }
onSelectAction = { state.eventSink(ConfigureRoomEvent.HandleAvatarAction(it)) }
)
AsyncActionView(
@@ -170,8 +170,8 @@ fun ConfigureRoomView(
},
onSuccess = { onCreateRoomSuccess(it) },
errorMessage = { stringResource(if (isSpace) R.string.screen_create_room_error_creating_space else R.string.screen_create_room_error_creating_room) },
onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom) },
onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) },
onRetry = { state.eventSink(ConfigureRoomEvent.CreateRoom) },
onErrorDismiss = { state.eventSink(ConfigureRoomEvent.CancelCreateRoom) },
)
PermissionsView(
@@ -12,7 +12,7 @@ import androidx.core.net.toUri
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.createroom.impl.configureroom.ConfigureRoomEvents
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvent
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
@@ -106,14 +106,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
assertThat(initialState.isValid).isFalse()
// Room name not empty
initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME))
initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME))
var newState: ConfigureRoomState = awaitItem()
config = config.copy(roomName = A_ROOM_NAME)
assertThat(newState.config).isEqualTo(config)
assertThat(newState.isValid).isTrue()
// Clear room name
newState.eventSink(ConfigureRoomEvents.RoomNameChanged(""))
newState.eventSink(ConfigureRoomEvent.RoomNameChanged(""))
newState = awaitItem()
config = config.copy(roomName = null)
assertThat(newState.config).isEqualTo(config)
@@ -136,13 +136,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
var expectedConfig = CreateRoomConfig()
assertThat(initialState.config).isEqualTo(expectedConfig)
// Room name
initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME))
initialState.eventSink(ConfigureRoomEvent.RoomNameChanged(A_ROOM_NAME))
var newState = awaitItem()
expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME)
assertThat(newState.config).isEqualTo(expectedConfig)
// Room topic
newState.eventSink(ConfigureRoomEvents.TopicChanged(A_MESSAGE))
newState.eventSink(ConfigureRoomEvent.TopicChanged(A_MESSAGE))
newState = awaitItem()
expectedConfig = expectedConfig.copy(topic = A_MESSAGE)
assertThat(newState.config).isEqualTo(expectedConfig)
@@ -153,7 +153,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
// From gallery
val uriFromGallery = AN_URI_FROM_GALLERY
pickerProvider.givenResult(uriFromGallery.toUri())
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
newState = awaitItem()
expectedConfig = expectedConfig.copy(avatarUri = uriFromGallery)
assertThat(newState.config).isEqualTo(expectedConfig)
@@ -161,7 +161,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
val uriFromCamera = AN_URI_FROM_CAMERA
pickerProvider.givenResult(uriFromCamera.toUri())
assertThat(newState.cameraPermissionState.permissionGranted).isFalse()
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto))
newState = awaitItem()
assertThat(newState.cameraPermissionState.showDialog).isTrue()
permissionsPresenter.setPermissionGranted()
@@ -173,18 +173,18 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
// Do it again, no permission is requested
val uriFromCamera2 = AN_URI_FROM_CAMERA_2
pickerProvider.givenResult(uriFromCamera2.toUri())
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.TakePhoto))
newState = awaitItem()
expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2)
assertThat(newState.config).isEqualTo(expectedConfig)
// Remove
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.Remove))
newState.eventSink(ConfigureRoomEvent.HandleAvatarAction(AvatarAction.Remove))
newState = awaitItem()
expectedConfig = expectedConfig.copy(avatarUri = null)
assertThat(newState.config).isEqualTo(expectedConfig)
// Room privacy
newState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
newState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
newState = awaitItem()
expectedConfig = expectedConfig.copy(
visibilityState = RoomVisibilityState.Public(
@@ -208,7 +208,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
matrixClient.givenCreateRoomResult(createRoomResult)
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
@@ -239,13 +239,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
// Use a public parent space so AskToJoin is a valid option
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace))
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin)
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
@@ -282,13 +282,13 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
// Use a public parent space so AskToJoin is a valid option
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(parentSpace))
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.AskToJoin))
assertThat(awaitItem().config.visibilityState.joinRuleItem).isEqualTo(JoinRuleItem.PublicVisibility.AskToJoin)
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
// We immediately receive the room power levels info needed for adding the child to a space
val powerLevels = RoomPowerLevels(
@@ -339,7 +339,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
matrixClient.givenCreateRoomResult(createRoomResult)
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
skipItems(2)
val analyticsEvent = analyticsService.capturedEvents.filterIsInstance<CreatedRoom>().firstOrNull()
@@ -369,14 +369,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk())))
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
@@ -399,14 +399,14 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
fakeMatrixClient.givenCreateRoomResult(createRoomResult)
// Create
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
assertThat((stateAfterCreateRoom.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
// Retry
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
stateAfterCreateRoom.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterRetry = awaitItem()
@@ -414,7 +414,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
assertThat((stateAfterRetry.createRoomAction as? AsyncAction.Failure)?.error).isEqualTo(createRoomResult.exceptionOrNull())
// Cancel
stateAfterRetry.eventSink(ConfigureRoomEvents.CancelCreateRoom)
stateAfterRetry.eventSink(ConfigureRoomEvent.CancelCreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
}
}
@@ -430,9 +430,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
)
presenter.test {
val initialState = initialState()
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
skipItems(1)
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("invalid address"))
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("invalid address"))
skipItems(1)
advanceUntilIdle()
awaitItem().also { state ->
@@ -450,9 +450,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
)
presenter.test {
val initialState = initialState()
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
skipItems(1)
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address"))
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address"))
skipItems(1)
advanceUntilIdle()
awaitItem().also { state ->
@@ -470,9 +470,9 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
)
presenter.test {
val initialState = initialState()
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
skipItems(1)
initialState.eventSink(ConfigureRoomEvents.RoomAddressChanged("address"))
initialState.eventSink(ConfigureRoomEvent.RoomAddressChanged("address"))
skipItems(1)
advanceUntilIdle()
awaitItem().also { state ->
@@ -488,22 +488,22 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
val initialState = initialState()
// First change the join rule to public
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
assertThat(awaitItem().config.visibilityState).isInstanceOf(RoomVisibilityState.Public::class.java)
// Then check changing the parent space resets it to private
// (via LaunchedEffect fallback since Public is not in availableJoinRules for non-public parent)
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom()))
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom()))
skipItems(1) // Skip intermediate state
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
// If we change the join rule back to public
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
skipItems(1) // Skip intermediate state (Public is still invalid)
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
// Then remove the parent space, the join rule stays private
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(null))
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(null))
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
}
}
@@ -514,7 +514,7 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
presenter.test {
val initialState = initialState()
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom()))
initialState.eventSink(ConfigureRoomEvent.SetParentSpace(aSpaceRoom()))
assertThat(awaitError())
}
@@ -534,8 +534,8 @@ class ConfigureRoomPresenterTest : RobolectricTest() {
presenter.test {
val initialState = initialState()
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private))
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
initialState.eventSink(ConfigureRoomEvent.JoinRuleChanged(JoinRuleItem.PrivateVisibility.Private))
initialState.eventSink(ConfigureRoomEvent.CreateRoom)
assertThat(awaitItem().createRoomAction.isLoading()).isTrue()
assertThat(awaitItem().createRoomAction.isSuccess()).isTrue()