diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusEvent.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusEvent.kt index 80dd02e08a..815cd12d66 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusEvent.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusEvent.kt @@ -10,18 +10,11 @@ package io.element.android.features.preferences.impl.userstatus import io.element.android.libraries.matrix.api.user.UserStatus sealed interface UserStatusEvent { - /** User tapped the status row — open the picker bottom sheet. */ - data object Open : UserStatusEvent - /** User dismissed the bottom sheet without selecting. */ - 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 OpenPicker : UserStatusEvent + data object DismissPicker : UserStatusEvent + data class SetStatus(val status: UserStatus) : UserStatusEvent data object OpenCustomInput : UserStatusEvent - /** User tapped Cancel in the inline custom input row. */ data object CancelCustomInput : UserStatusEvent - /** User changed the emoji in the custom input row. */ data class UpdateCustomEmoji(val emoji: String) : UserStatusEvent - /** User tapped the clear action on an existing status. */ - data object Clear : UserStatusEvent + data object ClearStatus : UserStatusEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt index 0bb626b159..384e09862a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenter.kt @@ -7,8 +7,8 @@ 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.rememberTextFieldState import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -18,11 +18,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.api.user.UserStatus import kotlinx.coroutines.launch @ContributesBinding(SessionScope::class) @@ -34,13 +32,13 @@ class UserStatusPresenter( override fun present(): UserStatusState { val userProfile by matrixClient.userProfile.collectAsState() var pickerState by remember { mutableStateOf(UserStatusPickerState.Hidden) } - val customTextFieldState = remember { TextFieldState() } + val customTextFieldState = rememberTextFieldState() val coroutineScope = rememberCoroutineScope() fun handleEvent(event: UserStatusEvent) { when (event) { - UserStatusEvent.Open -> pickerState = UserStatusPickerState.ShowingPicker - UserStatusEvent.Dismiss -> pickerState = UserStatusPickerState.Hidden + UserStatusEvent.OpenPicker -> pickerState = UserStatusPickerState.ShowingPicker + UserStatusEvent.DismissPicker -> pickerState = UserStatusPickerState.Hidden UserStatusEvent.OpenCustomInput -> { val raw = userProfile.rawStatus if (raw != null) { @@ -53,11 +51,11 @@ class UserStatusPresenter( textFieldState = customTextFieldState, ) } - is UserStatusEvent.Set -> { + is UserStatusEvent.SetStatus -> { pickerState = UserStatusPickerState.Hidden coroutineScope.launch { matrixClient.setUserStatus(event.status) } } - UserStatusEvent.Clear -> { + UserStatusEvent.ClearStatus -> { pickerState = UserStatusPickerState.Hidden coroutineScope.launch { matrixClient.clearUserStatus() } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt index b1fe11d1ed..f79ae86d8b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusState.kt @@ -22,7 +22,7 @@ sealed interface UserStatusPickerState { data object Hidden : UserStatusPickerState data object ShowingPicker : UserStatusPickerState data class CustomInput( - val emoji: String = "😀", + val emoji: String, val textFieldState: TextFieldState, ) : UserStatusPickerState } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt index 035861f243..e211fb1110 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusView.kt @@ -65,20 +65,20 @@ fun UserStatusRow( if (displayedStatus != null) { CurrentStatusRow( displayedStatus = displayedStatus, - onClick = { state.eventSink(UserStatusEvent.Open) }, - onClear = { state.eventSink(UserStatusEvent.Clear) }, + onClick = { state.eventSink(UserStatusEvent.OpenPicker) }, + onClear = { state.eventSink(UserStatusEvent.ClearStatus) }, modifier = modifier, ) } else { EmptyStatusRow( - onClick = { state.eventSink(UserStatusEvent.Open) }, + onClick = { state.eventSink(UserStatusEvent.OpenPicker) }, modifier = modifier, ) } if (pickerState == UserStatusPickerState.ShowingPicker) { UserStatusPickerBottomSheet( - onDismiss = { state.eventSink(UserStatusEvent.Dismiss) }, - onSelectPredefined = { status -> state.eventSink(UserStatusEvent.Set(status)) }, + onDismiss = { state.eventSink(UserStatusEvent.DismissPicker) }, + onSelectPredefined = { status -> state.eventSink(UserStatusEvent.SetStatus(status)) }, onSelectCustom = { state.eventSink(UserStatusEvent.OpenCustomInput) }, ) } @@ -90,7 +90,7 @@ fun UserStatusRow( onEmojiChange = { state.eventSink(UserStatusEvent.UpdateCustomEmoji(it)) }, onConfirm = { 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) }, diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt index 349361a61b..455aefe180 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/userstatus/UserStatusPresenterTest.kt @@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.api.user.UserStatus import io.element.android.libraries.matrix.test.FakeMatrixClient import kotlinx.coroutines.test.runTest import org.junit.Test -import androidx.compose.foundation.text.input.TextFieldState class UserStatusPresenterTest { @@ -40,7 +39,7 @@ class UserStatusPresenterTest { createPresenter().present() }.test { val state = awaitItem() - state.eventSink(UserStatusEvent.Open) + state.eventSink(UserStatusEvent.OpenPicker) assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.ShowingPicker) } } @@ -51,9 +50,9 @@ class UserStatusPresenterTest { createPresenter().present() }.test { val state = awaitItem() - state.eventSink(UserStatusEvent.Open) + state.eventSink(UserStatusEvent.OpenPicker) awaitItem() // ShowingPicker - state.eventSink(UserStatusEvent.Dismiss) + state.eventSink(UserStatusEvent.DismissPicker) assertThat(awaitItem().pickerState).isEqualTo(UserStatusPickerState.Hidden) } } @@ -65,7 +64,7 @@ class UserStatusPresenterTest { createPresenter(client).present() }.test { val status = UserStatus(emoji = "💬", text = "In a meeting") - awaitItem().eventSink(UserStatusEvent.Set(status)) + awaitItem().eventSink(UserStatusEvent.SetStatus(status)) val newState = awaitItem() assertThat(newState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(newState.displayedStatus).isEqualTo(DisplayedStatus.UserSet(status)) @@ -93,7 +92,7 @@ class UserStatusPresenterTest { createPresenter(client).present() }.test { 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 awaitItem().eventSink(UserStatusEvent.OpenCustomInput) val state = awaitItem() @@ -133,9 +132,9 @@ class UserStatusPresenterTest { createPresenter(client).present() }.test { 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 - awaitItem().eventSink(UserStatusEvent.Clear) + awaitItem().eventSink(UserStatusEvent.ClearStatus) val clearedState = awaitItem() assertThat(clearedState.pickerState).isEqualTo(UserStatusPickerState.Hidden) assertThat(clearedState.displayedStatus).isNull()