Address quality issues on user status

This commit is contained in:
ganfra
2026-06-19 15:14:37 +02:00
parent 4a143c1053
commit 29b8068d4a
7 changed files with 40 additions and 17 deletions
@@ -24,8 +24,8 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.R
import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.features.preferences.impl.user.UserPreferences
import io.element.android.features.preferences.impl.userstatus.UserStatusRow
import io.element.android.features.preferences.impl.userstatus.UserStatusState import io.element.android.features.preferences.impl.userstatus.UserStatusState
import io.element.android.features.preferences.impl.userstatus.UserStatusView
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
@@ -143,7 +143,7 @@ private fun ColumnScope.UserStatusSection(
color = ElementTheme.colors.bgSubtleSecondary, color = ElementTheme.colors.bgSubtleSecondary,
) )
} }
UserStatusRow( UserStatusView(
state = userStatusState, state = userStatusState,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.preferences.impl.userstatus
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.ContributesTo
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.SessionScope
@ContributesTo(SessionScope::class)
@BindingContainer
interface UserStatusBindingContainer {
@Binds
fun bindUserStatusPresenter(presenter: UserStatusPresenter): Presenter<UserStatusState>
}
@@ -17,13 +17,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember 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.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.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ContributesBinding(SessionScope::class) @Inject
class UserStatusPresenter( class UserStatusPresenter(
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
) : Presenter<UserStatusState> { ) : Presenter<UserStatusState> {
@@ -9,6 +9,7 @@ package io.element.android.features.preferences.impl.userstatus
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.runtime.Immutable
import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.R
import io.element.android.libraries.matrix.api.user.DisplayedStatus import io.element.android.libraries.matrix.api.user.DisplayedStatus
import io.element.android.libraries.matrix.api.user.UserStatus import io.element.android.libraries.matrix.api.user.UserStatus
@@ -20,6 +21,7 @@ data class UserStatusState(
val eventSink: (UserStatusEvent) -> Unit, val eventSink: (UserStatusEvent) -> Unit,
) )
@Immutable
sealed interface UserStatusPickerState { sealed interface UserStatusPickerState {
data object Hidden : UserStatusPickerState data object Hidden : UserStatusPickerState
data object ShowingPicker : UserStatusPickerState data object ShowingPicker : UserStatusPickerState
@@ -29,7 +31,7 @@ sealed interface UserStatusPickerState {
) : UserStatusPickerState ) : UserStatusPickerState
} }
enum class PredefinedUserStatus(val emoji: String, @param:StringRes val labelRes: Int) { enum class PredefinedUserStatus(val emoji: String, @StringRes val labelRes: Int) {
IN_A_MEETING("💬", R.string.common_user_status_in_a_meeting), IN_A_MEETING("💬", R.string.common_user_status_in_a_meeting),
FOCUS_TIME("💡", R.string.common_user_status_focus_time), FOCUS_TIME("💡", R.string.common_user_status_focus_time),
ON_THE_ROAD("🚙", R.string.common_user_status_on_the_road), ON_THE_ROAD("🚙", R.string.common_user_status_on_the_road),
@@ -61,7 +61,7 @@ import io.element.android.libraries.matrix.api.user.UserStatus
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
fun UserStatusRow( fun UserStatusView(
state: UserStatusState, state: UserStatusState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@@ -85,8 +85,8 @@ fun UserStatusRow(
UserStatusPickerBottomSheet( UserStatusPickerBottomSheet(
currentRawStatus = state.rawStatus, currentRawStatus = state.rawStatus,
onDismiss = { state.eventSink(UserStatusEvent.DismissPicker) }, onDismiss = { state.eventSink(UserStatusEvent.DismissPicker) },
onSelectPredefined = { status -> state.eventSink(UserStatusEvent.SetStatus(status)) }, onSelectPredefinedStatus = { status -> state.eventSink(UserStatusEvent.SetStatus(status)) },
onSelectCustom = { state.eventSink(UserStatusEvent.OpenCustomInput) }, onSelectCustomStatus = { state.eventSink(UserStatusEvent.OpenCustomInput) },
) )
} }
} }
@@ -95,7 +95,6 @@ fun UserStatusRow(
emoji = pickerState.emoji, emoji = pickerState.emoji,
textFieldState = pickerState.textFieldState, textFieldState = pickerState.textFieldState,
rawStatus = state.rawStatus, rawStatus = state.rawStatus,
onEmojiChange = { state.eventSink(UserStatusEvent.UpdateCustomEmoji(it)) },
onConfirm = { onConfirm = {
state.eventSink( state.eventSink(
UserStatusEvent.SetStatus(UserStatus(pickerState.emoji, pickerState.textFieldState.text.toString())) UserStatusEvent.SetStatus(UserStatus(pickerState.emoji, pickerState.textFieldState.text.toString()))
@@ -159,8 +158,8 @@ private fun CurrentStatusRow(
private fun UserStatusPickerBottomSheet( private fun UserStatusPickerBottomSheet(
currentRawStatus: UserStatus?, currentRawStatus: UserStatus?,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onSelectPredefined: (UserStatus) -> Unit, onSelectPredefinedStatus: (UserStatus) -> Unit,
onSelectCustom: () -> Unit, onSelectCustomStatus: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val sheetState = rememberModalBottomSheetState() val sheetState = rememberModalBottomSheetState()
@@ -191,7 +190,7 @@ private fun UserStatusPickerBottomSheet(
}, },
onClick = { onClick = {
sheetState.hide(coroutineScope) { sheetState.hide(coroutineScope) {
onSelectPredefined(predefinedUserStatus) onSelectPredefinedStatus(predefinedUserStatus)
} }
}, },
) )
@@ -203,7 +202,7 @@ private fun UserStatusPickerBottomSheet(
leadingContent = ListItemContent.Text(text = "✏️"), leadingContent = ListItemContent.Text(text = "✏️"),
onClick = { onClick = {
sheetState.hide(coroutineScope) { sheetState.hide(coroutineScope) {
onSelectCustom() onSelectCustomStatus()
} }
}, },
) )
@@ -217,7 +216,6 @@ private fun CustomStatusInputRow(
emoji: String, emoji: String,
textFieldState: TextFieldState, textFieldState: TextFieldState,
rawStatus: UserStatus?, rawStatus: UserStatus?,
onEmojiChange: (String) -> Unit,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onCancel: () -> Unit, onCancel: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -299,6 +297,6 @@ private fun CustomStatusInputRow(
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun UserStatusRowPreview(@PreviewParameter(UserStatusStateProvider::class) state: UserStatusState) = ElementPreview { internal fun UserStatusViewPreview(@PreviewParameter(UserStatusStateProvider::class) state: UserStatusState) = ElementPreview {
UserStatusRow(state = state) UserStatusView(state = state)
} }
@@ -8,8 +8,10 @@
package io.element.android.libraries.matrix.api.user package io.element.android.libraries.matrix.api.user
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Immutable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Immutable
sealed interface DisplayedStatus : Parcelable { sealed interface DisplayedStatus : Parcelable {
/** Status set manually by the user via m.status. */ /** Status set manually by the user via m.status. */
@Parcelize @Parcelize
@@ -138,6 +138,7 @@ import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParam
import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset
import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
@Suppress("LargeClass")
class RustMatrixClient( class RustMatrixClient(
override val sessionPaths: SessionPaths, override val sessionPaths: SessionPaths,
private val innerClient: Client, private val innerClient: Client,