Rename Event

This commit is contained in:
ganfra
2026-06-17 11:05:53 +02:00
parent 2a0e41652a
commit 553a4785e1
5 changed files with 24 additions and 34 deletions
@@ -10,18 +10,11 @@ package io.element.android.features.preferences.impl.userstatus
import io.element.android.libraries.matrix.api.user.UserStatus import io.element.android.libraries.matrix.api.user.UserStatus
sealed interface UserStatusEvent { sealed interface UserStatusEvent {
/** User tapped the status row — open the picker bottom sheet. */ data object OpenPicker : UserStatusEvent
data object Open : UserStatusEvent data object DismissPicker : UserStatusEvent
/** User dismissed the bottom sheet without selecting. */ data class SetStatus(val status: UserStatus) : UserStatusEvent
data object Dismiss : UserStatusEvent
/** User selected a predefined or confirmed a custom status. */
data class Set(val status: UserStatus) : UserStatusEvent
/** User tapped "Custom…" in the bottom sheet. */
data object OpenCustomInput : UserStatusEvent data object OpenCustomInput : UserStatusEvent
/** User tapped Cancel in the inline custom input row. */
data object CancelCustomInput : UserStatusEvent data object CancelCustomInput : UserStatusEvent
/** User changed the emoji in the custom input row. */
data class UpdateCustomEmoji(val emoji: String) : UserStatusEvent data class UpdateCustomEmoji(val emoji: String) : UserStatusEvent
/** User tapped the clear action on an existing status. */ data object ClearStatus : UserStatusEvent
data object Clear : UserStatusEvent
} }
@@ -7,8 +7,8 @@
package io.element.android.features.preferences.impl.userstatus package io.element.android.features.preferences.impl.userstatus
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -18,11 +18,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.UserStatus
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ContributesBinding(SessionScope::class) @ContributesBinding(SessionScope::class)
@@ -34,13 +32,13 @@ class UserStatusPresenter(
override fun present(): UserStatusState { override fun present(): UserStatusState {
val userProfile by matrixClient.userProfile.collectAsState() val userProfile by matrixClient.userProfile.collectAsState()
var pickerState by remember { mutableStateOf<UserStatusPickerState>(UserStatusPickerState.Hidden) } var pickerState by remember { mutableStateOf<UserStatusPickerState>(UserStatusPickerState.Hidden) }
val customTextFieldState = remember { TextFieldState() } val customTextFieldState = rememberTextFieldState()
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
fun handleEvent(event: UserStatusEvent) { fun handleEvent(event: UserStatusEvent) {
when (event) { when (event) {
UserStatusEvent.Open -> pickerState = UserStatusPickerState.ShowingPicker UserStatusEvent.OpenPicker -> pickerState = UserStatusPickerState.ShowingPicker
UserStatusEvent.Dismiss -> pickerState = UserStatusPickerState.Hidden UserStatusEvent.DismissPicker -> pickerState = UserStatusPickerState.Hidden
UserStatusEvent.OpenCustomInput -> { UserStatusEvent.OpenCustomInput -> {
val raw = userProfile.rawStatus val raw = userProfile.rawStatus
if (raw != null) { if (raw != null) {
@@ -53,11 +51,11 @@ class UserStatusPresenter(
textFieldState = customTextFieldState, textFieldState = customTextFieldState,
) )
} }
is UserStatusEvent.Set -> { is UserStatusEvent.SetStatus -> {
pickerState = UserStatusPickerState.Hidden pickerState = UserStatusPickerState.Hidden
coroutineScope.launch { matrixClient.setUserStatus(event.status) } coroutineScope.launch { matrixClient.setUserStatus(event.status) }
} }
UserStatusEvent.Clear -> { UserStatusEvent.ClearStatus -> {
pickerState = UserStatusPickerState.Hidden pickerState = UserStatusPickerState.Hidden
coroutineScope.launch { matrixClient.clearUserStatus() } coroutineScope.launch { matrixClient.clearUserStatus() }
} }
@@ -22,7 +22,7 @@ sealed interface UserStatusPickerState {
data object Hidden : UserStatusPickerState data object Hidden : UserStatusPickerState
data object ShowingPicker : UserStatusPickerState data object ShowingPicker : UserStatusPickerState
data class CustomInput( data class CustomInput(
val emoji: String = "😀", val emoji: String,
val textFieldState: TextFieldState, val textFieldState: TextFieldState,
) : UserStatusPickerState ) : UserStatusPickerState
} }
@@ -65,20 +65,20 @@ fun UserStatusRow(
if (displayedStatus != null) { if (displayedStatus != null) {
CurrentStatusRow( CurrentStatusRow(
displayedStatus = displayedStatus, displayedStatus = displayedStatus,
onClick = { state.eventSink(UserStatusEvent.Open) }, onClick = { state.eventSink(UserStatusEvent.OpenPicker) },
onClear = { state.eventSink(UserStatusEvent.Clear) }, onClear = { state.eventSink(UserStatusEvent.ClearStatus) },
modifier = modifier, modifier = modifier,
) )
} else { } else {
EmptyStatusRow( EmptyStatusRow(
onClick = { state.eventSink(UserStatusEvent.Open) }, onClick = { state.eventSink(UserStatusEvent.OpenPicker) },
modifier = modifier, modifier = modifier,
) )
} }
if (pickerState == UserStatusPickerState.ShowingPicker) { if (pickerState == UserStatusPickerState.ShowingPicker) {
UserStatusPickerBottomSheet( UserStatusPickerBottomSheet(
onDismiss = { state.eventSink(UserStatusEvent.Dismiss) }, onDismiss = { state.eventSink(UserStatusEvent.DismissPicker) },
onSelectPredefined = { status -> state.eventSink(UserStatusEvent.Set(status)) }, onSelectPredefined = { status -> state.eventSink(UserStatusEvent.SetStatus(status)) },
onSelectCustom = { state.eventSink(UserStatusEvent.OpenCustomInput) }, onSelectCustom = { state.eventSink(UserStatusEvent.OpenCustomInput) },
) )
} }
@@ -90,7 +90,7 @@ fun UserStatusRow(
onEmojiChange = { state.eventSink(UserStatusEvent.UpdateCustomEmoji(it)) }, onEmojiChange = { state.eventSink(UserStatusEvent.UpdateCustomEmoji(it)) },
onConfirm = { onConfirm = {
state.eventSink( state.eventSink(
UserStatusEvent.Set(UserStatus(pickerState.emoji, pickerState.textFieldState.text.toString())) UserStatusEvent.SetStatus(UserStatus(pickerState.emoji, pickerState.textFieldState.text.toString()))
) )
}, },
onCancel = { state.eventSink(UserStatusEvent.CancelCustomInput) }, onCancel = { state.eventSink(UserStatusEvent.CancelCustomInput) },
@@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.api.user.UserStatus
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
import androidx.compose.foundation.text.input.TextFieldState
class UserStatusPresenterTest { class UserStatusPresenterTest {
@@ -40,7 +39,7 @@ class UserStatusPresenterTest {
createPresenter().present() createPresenter().present()
}.test { }.test {
val state = awaitItem() val state = awaitItem()
state.eventSink(UserStatusEvent.Open) state.eventSink(UserStatusEvent.OpenPicker)
assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.ShowingPicker) assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.ShowingPicker)
} }
} }
@@ -51,9 +50,9 @@ class UserStatusPresenterTest {
createPresenter().present() createPresenter().present()
}.test { }.test {
val state = awaitItem() val state = awaitItem()
state.eventSink(UserStatusEvent.Open) state.eventSink(UserStatusEvent.OpenPicker)
awaitItem() // ShowingPicker awaitItem() // ShowingPicker
state.eventSink(UserStatusEvent.Dismiss) state.eventSink(UserStatusEvent.DismissPicker)
assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.Hidden)
} }
} }
@@ -65,7 +64,7 @@ class UserStatusPresenterTest {
createPresenter(client).present() createPresenter(client).present()
}.test { }.test {
val status = UserStatus(emoji = "💬", text = "In a meeting") val status = UserStatus(emoji = "💬", text = "In a meeting")
awaitItem().eventSink(UserStatusEvent.Set(status)) awaitItem().eventSink(UserStatusEvent.SetStatus(status))
val newState = awaitItem() val newState = awaitItem()
assertThat(newState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(newState.pickerState).isEqualTo(UserStatusPickerState.Hidden)
assertThat(newState.displayedStatus).isEqualTo(DisplayedStatus.UserSet(status)) assertThat(newState.displayedStatus).isEqualTo(DisplayedStatus.UserSet(status))
@@ -93,7 +92,7 @@ class UserStatusPresenterTest {
createPresenter(client).present() createPresenter(client).present()
}.test { }.test {
val status = UserStatus(emoji = "🌴", text = "Away") val status = UserStatus(emoji = "🌴", text = "Away")
awaitItem().eventSink(UserStatusEvent.Set(status)) awaitItem().eventSink(UserStatusEvent.SetStatus(status))
// Single emission: pickerState=Hidden and displayedStatus both set in one recomposition // Single emission: pickerState=Hidden and displayedStatus both set in one recomposition
awaitItem().eventSink(UserStatusEvent.OpenCustomInput) awaitItem().eventSink(UserStatusEvent.OpenCustomInput)
val state = awaitItem() val state = awaitItem()
@@ -133,9 +132,9 @@ class UserStatusPresenterTest {
createPresenter(client).present() createPresenter(client).present()
}.test { }.test {
val status = UserStatus(emoji = "☕", text = "Be right back") val status = UserStatus(emoji = "☕", text = "Be right back")
awaitItem().eventSink(UserStatusEvent.Set(status)) awaitItem().eventSink(UserStatusEvent.SetStatus(status))
// Single emission: pickerState=Hidden and displayedStatus both set in one recomposition // Single emission: pickerState=Hidden and displayedStatus both set in one recomposition
awaitItem().eventSink(UserStatusEvent.Clear) awaitItem().eventSink(UserStatusEvent.ClearStatus)
val clearedState = awaitItem() val clearedState = awaitItem()
assertThat(clearedState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(clearedState.pickerState).isEqualTo(UserStatusPickerState.Hidden)
assertThat(clearedState.displayedStatus).isNull() assertThat(clearedState.displayedStatus).isNull()