diff --git a/app/src/main/kotlin/com/wire/android/navigation/HomeDestination.kt b/app/src/main/kotlin/com/wire/android/navigation/HomeDestination.kt index bb5e1398546..3db5a7e8a03 100644 --- a/app/src/main/kotlin/com/wire/android/navigation/HomeDestination.kt +++ b/app/src/main/kotlin/com/wire/android/navigation/HomeDestination.kt @@ -38,6 +38,7 @@ sealed class HomeDestination( @DrawableRes val icon: Int, val isSearchable: Boolean = false, val withNewConversationFab: Boolean = false, + val withUserAvatar: Boolean = true, val direction: Direction ) { data object Conversations : HomeDestination( @@ -75,6 +76,7 @@ sealed class HomeDestination( data object Settings : HomeDestination( title = R.string.settings_screen_title, icon = R.drawable.ic_settings, + withUserAvatar = false, direction = SettingsScreenDestination ) diff --git a/app/src/main/kotlin/com/wire/android/ui/common/RowItemTemplate.kt b/app/src/main/kotlin/com/wire/android/ui/common/RowItemTemplate.kt index 524d16bc72d..8412d6eb229 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/RowItemTemplate.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/RowItemTemplate.kt @@ -20,6 +20,7 @@ package com.wire.android.ui.common import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.runtime.Composable @@ -37,6 +38,7 @@ fun RowItemTemplate( titleStartPadding: Dp = dimensions().spacing8x, subtitle: @Composable () -> Unit = {}, actions: @Composable () -> Unit = {}, + wrapTitleContentWidth: Boolean = false, clickable: Clickable = Clickable(false) {} ) { RowItem( @@ -46,12 +48,18 @@ fun RowItemTemplate( leadingIcon() Column( modifier = Modifier - .weight(1f) .padding(start = titleStartPadding) + .then( + if (wrapTitleContentWidth) Modifier.wrapContentWidth() else Modifier.weight(1f) + ) ) { title() subtitle() } + if (wrapTitleContentWidth) { + // Add a spacer to push the actions to the end of the row when weight is not set + Spacer(modifier = Modifier.weight(1f)) + } Box( modifier = Modifier .wrapContentWidth() diff --git a/app/src/main/kotlin/com/wire/android/ui/home/HomeTopBar.kt b/app/src/main/kotlin/com/wire/android/ui/home/HomeTopBar.kt index f5a247fced9..9f34130884c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/HomeTopBar.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/HomeTopBar.kt @@ -68,21 +68,28 @@ fun HomeTopBar( onButtonClicked = { onOpenConversationFilter(navigationItem.currentFilter()) } ) } - val openLabel = stringResource(R.string.content_description_open_label) + if (navigationItem.withUserAvatar) { + val openLabel = stringResource(R.string.content_description_open_label) val contentDescription = if (shouldShowCreateTeamUnreadIndicator) { stringResource(R.string.content_description_home_profile_btn_with_notification) } else { stringResource(R.string.content_description_home_profile_btn) } - UserProfileAvatar( - avatarData = userAvatarData, - clickable = remember { - Clickable(enabled = true, onClickDescription = openLabel) { onNavigateToSelfUserProfile() } - }, - type = UserProfileAvatarType.WithIndicators.RegularUser(legalHoldIndicatorVisible = withLegalHoldIndicator), - shouldShowCreateTeamUnreadIndicator = shouldShowCreateTeamUnreadIndicator, + UserProfileAvatar( + avatarData = userAvatarData, + clickable = remember { + Clickable( + enabled = true, + onClickDescription = openLabel + ) { onNavigateToSelfUserProfile() } + }, + type = UserProfileAvatarType.WithIndicators.RegularUser( + legalHoldIndicatorVisible = withLegalHoldIndicator + ), + shouldShowCreateTeamUnreadIndicator = shouldShowCreateTeamUnreadIndicator, contentDescription = contentDescription - ) + ) + } }, elevation = elevation, ) @@ -105,6 +112,23 @@ fun PreviewTopBar() { } } +@PreviewMultipleThemes +@Composable +fun PreviewSettingsTopBarWithoutAvatar() { + WireTheme { + HomeTopBar( + navigationItem = HomeDestination.Settings, + userAvatarData = UserAvatarData(null, UserAvailabilityStatus.AVAILABLE), + elevation = 0.dp, + withLegalHoldIndicator = false, + shouldShowCreateTeamUnreadIndicator = false, + onHamburgerMenuClick = {}, + onNavigateToSelfUserProfile = {}, + onOpenConversationFilter = {} + ) + } +} + @PreviewMultipleThemes @Composable fun PreviewTopBarWithNameBasedAvatar() { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt index e4d5abeaffa..d90df2654e4 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt @@ -19,6 +19,8 @@ package com.wire.android.ui.home.settings import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -29,6 +31,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import com.ramcosta.composedestinations.spec.Direction import com.wire.android.R import com.wire.android.model.Clickable @@ -64,18 +67,21 @@ fun SettingsItem( modifier: Modifier = Modifier, title: String? = null, @DrawableRes trailingIcon: Int? = null, + trailingText: String? = null, switchState: SwitchState = SwitchState.None, onRowPressed: Clickable = Clickable(false), onIconPressed: Clickable? = null ) { RowItemTemplate( modifier = modifier, + wrapTitleContentWidth = true, title = { if (!title.isNullOrBlank()) { Text( style = MaterialTheme.wireTypography.label01, color = MaterialTheme.wireColorScheme.secondaryText, text = title, + maxLines = 1, modifier = Modifier.padding(start = dimensions().spacing8x) ) } @@ -83,22 +89,43 @@ fun SettingsItem( style = MaterialTheme.wireTypography.body01, color = MaterialTheme.wireColorScheme.onBackground, text = text, + maxLines = 1, modifier = Modifier.padding(start = dimensions().spacing8x) ) }, actions = { - SettingsOptionSwitch(switchState = switchState) - trailingIcon?.let { - Icon( - painter = painterResource(id = trailingIcon), - contentDescription = "", - tint = MaterialTheme.wireColorScheme.onSecondaryButtonEnabled, - modifier = Modifier - .defaultMinSize(dimensions().wireIconButtonSize) - .padding(end = dimensions().spacing8x) - .clickable(onIconPressed) - ) - } ?: Icons.Filled.ChevronRight + Row( + horizontalArrangement = Arrangement.End, + ) { + SettingsOptionSwitch(switchState = switchState) + if (trailingText != null) { + Row( + Modifier + .padding(end = dimensions().spacing12x) + .weight(1f), + horizontalArrangement = Arrangement.End + ) { + Text( + trailingText, + style = MaterialTheme.wireTypography.body01, + color = MaterialTheme.wireColorScheme.secondaryText, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + } + } + trailingIcon?.let { + Icon( + painter = painterResource(id = trailingIcon), + contentDescription = "", + tint = MaterialTheme.wireColorScheme.onSecondaryButtonEnabled, + modifier = Modifier + .defaultMinSize(dimensions().wireIconButtonSize) + .padding(end = dimensions().spacing8x) + .clickable(onIconPressed) + ) + } ?: Icons.Filled.ChevronRight + } }, clickable = onRowPressed ) @@ -229,7 +256,7 @@ sealed class SettingsItem(open val id: String, open val title: UIText) { @PreviewMultipleThemes @Composable -fun PreviewFileRestrictionDialog() { +fun PreviewSettingsItem() { WireTheme { SettingsItem( title = "Some Setting", @@ -238,3 +265,29 @@ fun PreviewFileRestrictionDialog() { ) } } + +@PreviewMultipleThemes +@Composable +fun PreviewSettingsItemTrailingComposable() { + WireTheme { + SettingsItem( + title = "Some Setting", + text = "This is the value of the setting", + trailingIcon = R.drawable.ic_arrow_right, + trailingText = "Longlonglonglonglonglonglonglong Name" + ) + } +} + +@PreviewMultipleThemes +@Composable +fun PreviewSettingsItemTrailingShortComposable() { + WireTheme { + SettingsItem( + title = "Some Setting", + text = "This is the value of the setting", + trailingIcon = R.drawable.ic_arrow_right, + trailingText = "Short Name" + ) + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt index 4d6892641d2..10afe2caad6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsScreen.kt @@ -105,6 +105,12 @@ fun SettingsScreenContent( add(SettingsItem.BackupAndRestore) } }, + trailingText = { settingsItem -> + if (settingsItem is SettingsItem.YourAccount) { + return@folderWithElements settingsState.userName + } + return@folderWithElements null + }, onItemClicked = onItemClicked ) @@ -160,6 +166,7 @@ fun SettingsScreenContent( private fun LazyListScope.folderWithElements( header: String, items: List, + trailingText: ((SettingsItem) -> String?)? = null, onItemClicked: (SettingsItem.DirectionItem) -> Unit ) { folderWithElements( @@ -175,6 +182,7 @@ private fun LazyListScope.folderWithElements( } }, trailingIcon = if (settingsItem is SettingsItem.DirectionItem) R.drawable.ic_arrow_right else null, + trailingText = trailingText?.invoke(settingsItem), ) } } @@ -182,5 +190,9 @@ private fun LazyListScope.folderWithElements( @PreviewMultipleThemes @Composable fun PreviewSettingsScreen() = WireTheme { - SettingsScreenContent(settingsState = SettingsState(), onItemClicked = {}, onAppLockSwitchChanged = {},) + SettingsScreenContent( + settingsState = SettingsState(userName = "Longlonglonglonglonglonglonglong Name"), + onItemClicked = {}, + onAppLockSwitchChanged = {}, + ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsState.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsState.kt index 5552bd135be..4b6abbf1e9a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsState.kt @@ -19,5 +19,6 @@ package com.wire.android.ui.home.settings data class SettingsState( val isAppLockEditable: Boolean = false, - val isAppLockEnabled: Boolean = false + val isAppLockEnabled: Boolean = false, + val userName: String = "", ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt index df18901d052..4aa4b919ddb 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt @@ -24,16 +24,23 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.wire.android.datastore.GlobalDataStore +import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.kalium.logic.feature.featureConfig.ObserveIsAppLockEditableUseCase +import com.wire.kalium.logic.feature.user.GetSelfUserUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SettingsViewModel @Inject constructor( private val globalDataStore: GlobalDataStore, - private val observeIsAppLockEditable: ObserveIsAppLockEditableUseCase + private val observeIsAppLockEditable: ObserveIsAppLockEditableUseCase, + private val getSelf: GetSelfUserUseCase, + private val dispatchers: DispatcherProvider, ) : ViewModel() { var state by mutableStateOf(SettingsState()) private set @@ -51,6 +58,9 @@ class SettingsViewModel @Inject constructor( ) }.collect { state = it } } + viewModelScope.launch { + fetchSelfUser() + } } fun disableAppLock() { @@ -58,4 +68,15 @@ class SettingsViewModel @Inject constructor( globalDataStore.clearAppLockPasscode() } } + + private suspend fun fetchSelfUser() { + viewModelScope.launch { + val self = + getSelf().flowOn(dispatchers.io()).shareIn(this, SharingStarted.WhileSubscribed(1)) + + self.collect { user -> + state = state.copy(userName = user.name ?: "") + } + } + } }