implement show/hide header on scroll in LibraryScreen and prevent haptic feedback on re-selecting the current bottom bar item

This commit is contained in:
jonasgaudian
2026-02-16 17:56:49 +01:00
parent eae37715cd
commit 47d7e01f7f
4 changed files with 87 additions and 43 deletions

View File

@@ -207,8 +207,10 @@ fun BottomNavigationBar(
NavigationBarItem( NavigationBarItem(
selected = isSelected, selected = isSelected,
onClick = { onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress) if (!isSelected) {
if (screen == Screen.More) showMoreMenu = true else onItemSelected(screen) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
if (screen == Screen.More) showMoreMenu = true else onItemSelected(screen)
}
}, },
label = if (showLabels) { label = if (showLabels) {
{ {

View File

@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@@ -315,6 +316,7 @@ fun AllCardsView(
onItemClick: (VocabularyItem) -> Unit, onItemClick: (VocabularyItem) -> Unit,
onItemLongClick: (VocabularyItem) -> Unit, onItemLongClick: (VocabularyItem) -> Unit,
onDeleteClick: (VocabularyItem) -> Unit, onDeleteClick: (VocabularyItem) -> Unit,
listState: LazyListState,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
if (vocabularyItems.isEmpty()) { if (vocabularyItems.isEmpty()) {
@@ -340,6 +342,7 @@ fun AllCardsView(
} }
} else { } else {
LazyColumn( LazyColumn(
state = listState,
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = 100.dp) contentPadding = PaddingValues(bottom = 100.dp)

View File

@@ -4,6 +4,10 @@ package eu.gaudian.translator.view.library
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -42,6 +46,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -122,6 +127,10 @@ fun LibraryScreen(
val vocabularyItems by vocabularyItemsFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val vocabularyItems by vocabularyItemsFlow.collectAsStateWithLifecycle(initialValue = emptyList())
var isHeaderVisible by remember { mutableStateOf(true) }
var previousIndex by remember { mutableStateOf(0) }
var previousScrollOffset by remember { mutableStateOf(0) }
// Set navigation context when vocabulary items are loaded // Set navigation context when vocabulary items are loaded
LaunchedEffect(vocabularyItems) { LaunchedEffect(vocabularyItems) {
if (vocabularyItems.isNotEmpty()) { if (vocabularyItems.isNotEmpty()) {
@@ -129,6 +138,24 @@ fun LibraryScreen(
} }
} }
LaunchedEffect(isCategoriesView, isInSelectionMode) {
if (isCategoriesView || isInSelectionMode) {
isHeaderVisible = true
}
}
LaunchedEffect(lazyListState, isCategoriesView, isInSelectionMode) {
if (isCategoriesView || isInSelectionMode) return@LaunchedEffect
snapshotFlow { lazyListState.firstVisibleItemIndex to lazyListState.firstVisibleItemScrollOffset }
.collect { (index, offset) ->
val isScrollingDown = index > previousIndex || (index == previousIndex && offset > previousScrollOffset)
val isAtTop = index == 0 && offset <= 4
isHeaderVisible = if (isAtTop) true else !isScrollingDown
previousIndex = index
previousScrollOffset = offset
}
}
Box( Box(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.TopCenter contentAlignment = Alignment.TopCenter
@@ -139,47 +166,55 @@ fun LibraryScreen(
.fillMaxSize() .fillMaxSize()
.padding(horizontal = 24.dp), .padding(horizontal = 24.dp),
) { ) {
Spacer(modifier = Modifier.height(24.dp)) AnimatedVisibility(
visible = isHeaderVisible,
if (isInSelectionMode) { enter = fadeIn() + expandVertically(),
SelectionTopBar( exit = fadeOut() + shrinkVertically()
selectionCount = selection.size, ) {
onCloseClick = { selection = emptySet() }, Column {
onSelectAllClick = { Spacer(modifier = Modifier.height(24.dp))
selection = if (selection.size == vocabularyItems.size) emptySet()
else vocabularyItems.map { it.id.toLong() }.toSet() if (isInSelectionMode) {
}, SelectionTopBar(
onDeleteClick = { selectionCount = selection.size,
vocabularyViewModel.deleteVocabularyItemsById(selection.map { it.toInt() }) onCloseClick = { selection = emptySet() },
selection = emptySet() onSelectAllClick = {
}, selection = if (selection.size == vocabularyItems.size) emptySet()
onMoveToCategoryClick = { showCategoryDialog = true }, else vocabularyItems.map { it.id.toLong() }.toSet()
onMoveToStageClick = { showStageDialog = true }, },
isRemoveEnabled = false, onDeleteClick = {
onRemoveFromCategoryClick = {} vocabularyViewModel.deleteVocabularyItemsById(selection.map { it.toInt() })
) selection = emptySet()
} else { },
LibraryTopBar( onMoveToCategoryClick = { showCategoryDialog = true },
onAddClick = { /* TODO: Add new card/category */ } onMoveToStageClick = { showStageDialog = true },
) isRemoveEnabled = false,
onRemoveFromCategoryClick = {}
)
} else {
LibraryTopBar(
onAddClick = { /* TODO: Add new card/category */ }
)
}
Spacer(modifier = Modifier.height(24.dp))
SearchBar(
searchQuery = filterState.searchQuery,
onQueryChanged = { filterState = filterState.copy(searchQuery = it) },
onFilterClick = { showFilterSheet = true }
)
Spacer(modifier = Modifier.height(24.dp))
SegmentedControl(
isCategoriesView = isCategoriesView,
onTabSelected = { isCategoriesView = it }
)
Spacer(modifier = Modifier.height(24.dp))
}
} }
Spacer(modifier = Modifier.height(24.dp))
SearchBar(
searchQuery = filterState.searchQuery,
onQueryChanged = { filterState = filterState.copy(searchQuery = it) },
onFilterClick = { showFilterSheet = true }
)
Spacer(modifier = Modifier.height(24.dp))
SegmentedControl(
isCategoriesView = isCategoriesView,
onTabSelected = { isCategoriesView = it }
)
Spacer(modifier = Modifier.height(24.dp))
LibraryViewContainer( LibraryViewContainer(
isCategoriesView = isCategoriesView, isCategoriesView = isCategoriesView,
@@ -199,6 +234,7 @@ fun LibraryScreen(
vocabularyItems = vocabularyItems, vocabularyItems = vocabularyItems,
allLanguages = allLanguages, allLanguages = allLanguages,
selection = selection, selection = selection,
listState = lazyListState,
onItemClick = { item -> onItemClick = { item ->
if (isInSelectionMode) { if (isInSelectionMode) {
selection = if (selection.contains(item.id.toLong())) { selection = if (selection.contains(item.id.toLong())) {

View File

@@ -268,6 +268,7 @@ fun NewVocListScreen(
vocabularyItems = vocabularyItems, vocabularyItems = vocabularyItems,
allLanguages = allLanguages, allLanguages = allLanguages,
selection = selection, selection = selection,
listState = lazyListState,
onItemClick = { item -> onItemClick = { item ->
if (isInSelectionMode) { if (isInSelectionMode) {
selection = if (selection.contains(item.id.toLong())) { selection = if (selection.contains(item.id.toLong())) {
@@ -588,7 +589,8 @@ fun AllCardsView(
selection: Set<Long>, selection: Set<Long>,
onItemClick: (VocabularyItem) -> Unit, onItemClick: (VocabularyItem) -> Unit,
onItemLongClick: (VocabularyItem) -> Unit, onItemLongClick: (VocabularyItem) -> Unit,
onDeleteClick: (VocabularyItem) -> Unit onDeleteClick: (VocabularyItem) -> Unit,
listState: androidx.compose.foundation.lazy.LazyListState
) { ) {
if (vocabularyItems.isEmpty()) { if (vocabularyItems.isEmpty()) {
Column( Column(
@@ -613,6 +615,7 @@ fun AllCardsView(
} }
} else { } else {
LazyColumn( LazyColumn(
state = listState,
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = 100.dp) contentPadding = PaddingValues(bottom = 100.dp)