Floating toolbar (#6147)
* Use floating toolbar on homepage * Fix deprecation issue * Create HorizontalFloatingToolbar wrapper in our components. * Fix Konsist test. * Fix compilation issue after rebase. * Fix lint issue. `floatingActionButton` must be the last parameter. * Add Preview for the case empty space. * Fix navigation bar overlapping buttons in empty space view. * Increase content padding, and apply it to the space tab too. * Update screenshots --------- Co-authored-by: chelsea <git@cdhildit.ch> Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
commit
e56c7e1227
50 changed files with 395 additions and 165 deletions
|
|
@ -33,7 +33,6 @@ data class HomeState(
|
|||
val eventSink: (HomeEvent) -> Unit,
|
||||
) {
|
||||
val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected
|
||||
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
|
||||
val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters
|
||||
val showNavigationBar = homeSpacesState.canCreateSpaces || homeSpacesState.spaceRooms.isNotEmpty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,12 @@ open class HomeStateProvider : PreviewParameterProvider<HomeState> {
|
|||
),
|
||||
) + RoomListStateProvider().values.map {
|
||||
aHomeState(roomListState = it)
|
||||
}
|
||||
} + aHomeState(
|
||||
currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces,
|
||||
homeSpacesState = aHomeSpacesState(
|
||||
spaceRooms = emptyList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aHomeState(
|
||||
|
|
|
|||
|
|
@ -21,19 +21,22 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.chrisbanes.haze.hazeEffect
|
||||
import dev.chrisbanes.haze.hazeSource
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
|
|
@ -58,15 +61,15 @@ import io.element.android.libraries.androidutils.throttler.FirstThrottler
|
|||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbar
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbarItem
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbarSeparator
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.NavigationBar
|
||||
import io.element.android.libraries.designsystem.theme.components.NavigationBarIcon
|
||||
import io.element.android.libraries.designsystem.theme.components.NavigationBarItem
|
||||
import io.element.android.libraries.designsystem.theme.components.NavigationBarText
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
|
|
@ -185,12 +188,10 @@ private fun HomeScaffold(
|
|||
onAccountSwitch = {
|
||||
state.eventSink(HomeEvent.SwitchToAccount(it))
|
||||
},
|
||||
onCreateSpace = onCreateSpaceClick,
|
||||
scrollBehavior = scrollBehavior,
|
||||
displayFilters = state.displayRoomListFilters,
|
||||
filtersState = roomListState.filtersState,
|
||||
spaceFiltersState = roomListState.spaceFiltersState,
|
||||
canCreateSpaces = state.homeSpacesState.canCreateSpaces,
|
||||
canReportBug = state.canReportBug,
|
||||
modifier = Modifier.hazeEffect(
|
||||
state = hazeState,
|
||||
|
|
@ -198,7 +199,7 @@ private fun HomeScaffold(
|
|||
)
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
floatingActionButton = {
|
||||
if (state.showNavigationBar) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
HomeBottomBar(
|
||||
|
|
@ -222,14 +223,29 @@ private fun HomeScaffold(
|
|||
state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.hazeEffect(
|
||||
state = hazeState,
|
||||
style = HazeMaterials.thick(),
|
||||
)
|
||||
floatingActionButton = when (state.currentHomeNavigationBarItem) {
|
||||
HomeNavigationBarItem.Chats -> {
|
||||
{
|
||||
HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_room)
|
||||
}
|
||||
}
|
||||
HomeNavigationBarItem.Spaces -> if (state.homeSpacesState.canCreateSpaces) {
|
||||
{
|
||||
HomeFloatingActionButton(onCreateSpaceClick, CommonStrings.action_create_space)
|
||||
}
|
||||
} else {
|
||||
// No FAB for spaces if we cannot create spaces
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
content = { padding ->
|
||||
val contentPadding = PaddingValues(
|
||||
bottom = 112.dp,
|
||||
)
|
||||
when (state.currentHomeNavigationBarItem) {
|
||||
HomeNavigationBarItem.Chats -> {
|
||||
RoomListContentView(
|
||||
|
|
@ -243,15 +259,7 @@ private fun HomeScaffold(
|
|||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = ::onRoomClick,
|
||||
onCreateRoomClick = onStartChatClick,
|
||||
contentPadding = PaddingValues(
|
||||
// FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80,
|
||||
// and include provided bottom padding
|
||||
// Disable contentPadding due to navigation issue using the keyboard
|
||||
// See https://issuetracker.google.com/issues/436432313
|
||||
bottom = 80.dp,
|
||||
// bottom = 80.dp + padding.calculateBottomPadding(),
|
||||
// top = padding.calculateTopPadding()
|
||||
),
|
||||
contentPadding = contentPadding,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
PaddingValues(
|
||||
|
|
@ -274,6 +282,7 @@ private fun HomeScaffold(
|
|||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.hazeSource(state = hazeState),
|
||||
contentPadding = contentPadding,
|
||||
state = state.homeSpacesState,
|
||||
lazyListState = spacesLazyListState,
|
||||
onSpaceClick = { spaceId ->
|
||||
|
|
@ -286,49 +295,48 @@ private fun HomeScaffold(
|
|||
}
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (state.displayActions) {
|
||||
FloatingActionButton(
|
||||
onClick = onStartChatClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Plus(),
|
||||
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeFloatingActionButton(
|
||||
onClick: () -> Unit,
|
||||
contentDescription: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FloatingActionButton(onClick = onClick, modifier = modifier) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Plus(),
|
||||
contentDescription = stringResource(id = contentDescription),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
private fun HomeBottomBar(
|
||||
currentHomeNavigationBarItem: HomeNavigationBarItem,
|
||||
onItemClick: (HomeNavigationBarItem) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
floatingActionButton: (@Composable () -> Unit)?,
|
||||
) {
|
||||
NavigationBar(
|
||||
containerColor = Color.Transparent,
|
||||
HorizontalFloatingToolbar(
|
||||
floatingActionButton = floatingActionButton,
|
||||
modifier = modifier
|
||||
.padding(bottom = ScreenOffset)
|
||||
.zIndex(1f),
|
||||
) {
|
||||
HomeNavigationBarItem.entries.forEach { item ->
|
||||
HomeNavigationBarItem.entries.forEachIndexed { index, item ->
|
||||
if (index > 0) {
|
||||
HorizontalFloatingToolbarSeparator()
|
||||
}
|
||||
val isSelected = currentHomeNavigationBarItem == item
|
||||
NavigationBarItem(
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
onItemClick(item)
|
||||
},
|
||||
icon = {
|
||||
NavigationBarIcon(
|
||||
imageVector = item.icon(isSelected),
|
||||
)
|
||||
},
|
||||
label = {
|
||||
NavigationBarText(
|
||||
text = stringResource(item.labelRes),
|
||||
)
|
||||
}
|
||||
HorizontalFloatingToolbarItem(
|
||||
icon = item.icon(isSelected),
|
||||
tooltipLabel = stringResource(item.labelRes),
|
||||
isSelected = isSelected,
|
||||
onClick = { onItemClick(item) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,9 +87,7 @@ fun HomeTopBar(
|
|||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
onAccountSwitch: (SessionId) -> Unit,
|
||||
onCreateSpace: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
canCreateSpaces: Boolean,
|
||||
canReportBug: Boolean,
|
||||
displayFilters: Boolean,
|
||||
filtersState: RoomListFiltersState,
|
||||
|
|
@ -134,17 +132,13 @@ fun HomeTopBar(
|
|||
)
|
||||
},
|
||||
actions = {
|
||||
when (selectedNavigationItem) {
|
||||
HomeNavigationBarItem.Chats -> RoomListMenuItems(
|
||||
if (selectedNavigationItem == HomeNavigationBarItem.Chats) {
|
||||
RoomListMenuItems(
|
||||
onToggleSearch = onToggleSearch,
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
canReportBug = canReportBug,
|
||||
spaceFiltersState = spaceFiltersState,
|
||||
)
|
||||
HomeNavigationBarItem.Spaces -> SpacesMenuItems(
|
||||
canCreateSpaces = canCreateSpaces,
|
||||
onCreateSpace = onCreateSpace
|
||||
)
|
||||
}
|
||||
},
|
||||
// We want a 16dp left padding for the navigationIcon :
|
||||
|
|
@ -230,21 +224,6 @@ private fun RoomListMenuItems(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpacesMenuItems(
|
||||
canCreateSpaces: Boolean,
|
||||
onCreateSpace: () -> Unit
|
||||
) {
|
||||
if (canCreateSpaces) {
|
||||
IconButton(onClick = onCreateSpace) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Plus(),
|
||||
contentDescription = stringResource(CommonStrings.action_create_space)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpaceFilterButton(
|
||||
spaceFiltersState: SpaceFiltersState,
|
||||
|
|
@ -365,8 +344,6 @@ internal fun HomeTopBarPreview() = ElementPreview {
|
|||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onToggleSearch = {},
|
||||
onCreateSpace = {},
|
||||
canCreateSpaces = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
|
|
@ -388,8 +365,6 @@ internal fun HomeTopBarSpaceFiltersSelectedPreview() = ElementPreview {
|
|||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onToggleSearch = {},
|
||||
onCreateSpace = {},
|
||||
canCreateSpaces = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
|
|
@ -411,8 +386,6 @@ internal fun HomeTopBarSpacesPreview() = ElementPreview {
|
|||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onToggleSearch = {},
|
||||
onCreateSpace = {},
|
||||
canCreateSpaces = true,
|
||||
canReportBug = true,
|
||||
displayFilters = false,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
|
|
@ -434,8 +407,6 @@ internal fun HomeTopBarWithIndicatorPreview() = ElementPreview {
|
|||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onToggleSearch = {},
|
||||
onCreateSpace = {},
|
||||
canCreateSpaces = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
|
|
@ -457,8 +428,6 @@ internal fun HomeTopBarMultiAccountPreview() = ElementPreview {
|
|||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onToggleSearch = {},
|
||||
onCreateSpace = {},
|
||||
canCreateSpaces = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.features.home.impl.spaces
|
|||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -48,6 +49,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
fun HomeSpacesView(
|
||||
state: HomeSpacesState,
|
||||
lazyListState: LazyListState,
|
||||
contentPadding: PaddingValues,
|
||||
onSpaceClick: (RoomId) -> Unit,
|
||||
onCreateSpaceClick: () -> Unit,
|
||||
onExploreClick: () -> Unit,
|
||||
|
|
@ -55,7 +57,7 @@ fun HomeSpacesView(
|
|||
) {
|
||||
if (state.canCreateSpaces && state.spaceRooms.isEmpty()) {
|
||||
EmptySpaceHomeView(
|
||||
modifier = modifier,
|
||||
modifier = modifier.padding(contentPadding),
|
||||
onCreateSpaceClick = onCreateSpaceClick,
|
||||
onExploreClick = onExploreClick,
|
||||
canExploreSpaces = state.canExploreSpaces,
|
||||
|
|
@ -63,7 +65,8 @@ fun HomeSpacesView(
|
|||
} else {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
state = lazyListState
|
||||
state = lazyListState,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
val space = state.space
|
||||
when (space) {
|
||||
|
|
@ -115,6 +118,9 @@ fun HomeSpacesView(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=1763-74215&t=9IGKMXHDfTGAqzQK-4
|
||||
*/
|
||||
@Composable
|
||||
private fun EmptySpaceHomeView(
|
||||
onCreateSpaceClick: () -> Unit,
|
||||
|
|
@ -159,8 +165,7 @@ private fun EmptySpaceHomeView(
|
|||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
|
|
@ -174,5 +179,6 @@ internal fun HomeSpacesViewPreview(
|
|||
onSpaceClick = {},
|
||||
onCreateSpaceClick = {},
|
||||
onExploreClick = {},
|
||||
contentPadding = PaddingValues(bottom = 112.dp),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue