fix(sources-search): search box visibility glitch when scrolling (#1278)
* fix(sources-screen): search box visibility glitch when scrolling * Refactor to a reusable function * Apply to MigrateSourceScreen * Fix warning * suggestions * Revert "Fix warning" This reverts commit b1c97bcf04b9e34b3eb314fd54c45be953d2f451. * Add comment --------- Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
parent
798bfc19ff
commit
b24b14c73d
4 changed files with 153 additions and 103 deletions
|
|
@ -3,6 +3,7 @@ package eu.kanade.presentation.browse
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -30,6 +31,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.model.installedExtension
|
||||
import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||
|
|
@ -48,7 +50,6 @@ import tachiyomi.i18n.kmk.KMR
|
|||
import tachiyomi.presentation.core.components.Badge
|
||||
import tachiyomi.presentation.core.components.BadgeGroup
|
||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||
import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.icons.FlagEmoji
|
||||
|
|
@ -126,100 +127,105 @@ private fun MigrateSourceList(
|
|||
}
|
||||
|
||||
Column(
|
||||
// Wrap around so we can use stickyHeader
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
) {
|
||||
AnimatedFloatingSearchBox(
|
||||
listState = lazyListState,
|
||||
searchQuery = state.searchQuery,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
placeholderText = stringResource(KMR.strings.action_search_for_source),
|
||||
// KMK <--
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = MaterialTheme.padding.medium,
|
||||
vertical = MaterialTheme.padding.small,
|
||||
),
|
||||
)
|
||||
|
||||
FastScrollLazyColumn(
|
||||
state = lazyListState,
|
||||
// contentPadding = contentPadding + topSmallPaddingValues,
|
||||
// KMK <--
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = MaterialTheme.padding.medium),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
stickyHeader(key = STICKY_HEADER_KEY_PREFIX) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = MaterialTheme.padding.medium),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.migration_selection_prompt),
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.header,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.migration_selection_prompt),
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.header,
|
||||
)
|
||||
|
||||
// KMK -->
|
||||
IconButton(onClick = { filterObsoleteSource = !filterObsoleteSource }) {
|
||||
Icon(
|
||||
Icons.Outlined.NewReleases,
|
||||
contentDescription = stringResource(MR.strings.ext_obsolete),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
.takeIf { filterObsoleteSource } ?: LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
// KMK -->
|
||||
IconButton(onClick = { filterObsoleteSource = !filterObsoleteSource }) {
|
||||
Icon(
|
||||
Icons.Outlined.NewReleases,
|
||||
contentDescription = stringResource(MR.strings.ext_obsolete),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
.takeIf { filterObsoleteSource } ?: LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
// KMK <--
|
||||
IconButton(onClick = onToggleSortingMode) {
|
||||
when (sortingMode) {
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
||||
Icons.Outlined.SortByAlpha,
|
||||
contentDescription = stringResource(MR.strings.action_sort_alpha),
|
||||
)
|
||||
SetMigrateSorting.Mode.TOTAL -> Icon(
|
||||
Icons.Outlined.Numbers,
|
||||
contentDescription = stringResource(MR.strings.action_sort_count),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = onToggleSortingDirection) {
|
||||
when (sortingDirection) {
|
||||
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
||||
Icons.Outlined.ArrowUpward,
|
||||
contentDescription = stringResource(MR.strings.action_asc),
|
||||
)
|
||||
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
||||
Icons.Outlined.ArrowDownward,
|
||||
contentDescription = stringResource(MR.strings.action_desc),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
Box {
|
||||
FastScrollLazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(top = 65.dp),
|
||||
// KMK <--
|
||||
) {
|
||||
items(
|
||||
items = list
|
||||
// KMK -->
|
||||
.filter {
|
||||
!filterObsoleteSource ||
|
||||
(
|
||||
it.first.installedExtension?.isObsolete != false &&
|
||||
(!isHentaiEnabled || it.first.id !in (EHENTAI_EXT_SOURCES.keys + EXHENTAI_EXT_SOURCES.keys))
|
||||
)
|
||||
},
|
||||
// KMK <--
|
||||
IconButton(onClick = onToggleSortingMode) {
|
||||
when (sortingMode) {
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
||||
Icons.Outlined.SortByAlpha,
|
||||
contentDescription = stringResource(MR.strings.action_sort_alpha),
|
||||
)
|
||||
SetMigrateSorting.Mode.TOTAL -> Icon(
|
||||
Icons.Outlined.Numbers,
|
||||
contentDescription = stringResource(MR.strings.action_sort_count),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = onToggleSortingDirection) {
|
||||
when (sortingDirection) {
|
||||
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
||||
Icons.Outlined.ArrowUpward,
|
||||
contentDescription = stringResource(MR.strings.action_asc),
|
||||
)
|
||||
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
||||
Icons.Outlined.ArrowDownward,
|
||||
contentDescription = stringResource(MR.strings.action_desc),
|
||||
)
|
||||
}
|
||||
}
|
||||
key = { (source, _) -> "migrate-${source.id}" },
|
||||
) { (source, count) ->
|
||||
MigrateSourceItem(
|
||||
// KMK -->
|
||||
// modifier = Modifier.animateItem(),
|
||||
modifier = Modifier.animateItemFastScroll(),
|
||||
// KMK <--
|
||||
source = source,
|
||||
count = count,
|
||||
onClickItem = { onClickItem(source) },
|
||||
onLongClickItem = { onLongClickItem(source) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = list
|
||||
// KMK -->
|
||||
.filter {
|
||||
!filterObsoleteSource ||
|
||||
(
|
||||
it.first.installedExtension?.isObsolete != false &&
|
||||
(!isHentaiEnabled || it.first.id !in (EHENTAI_EXT_SOURCES.keys + EXHENTAI_EXT_SOURCES.keys))
|
||||
)
|
||||
},
|
||||
// KMK <--
|
||||
key = { (source, _) -> "migrate-${source.id}" },
|
||||
) { (source, count) ->
|
||||
MigrateSourceItem(
|
||||
// KMK -->
|
||||
// modifier = Modifier.animateItem(),
|
||||
modifier = Modifier.animateItemFastScroll(),
|
||||
// KMK <--
|
||||
source = source,
|
||||
count = count,
|
||||
onClickItem = { onClickItem(source) },
|
||||
onLongClickItem = { onLongClickItem(source) },
|
||||
)
|
||||
}
|
||||
// KMK -->
|
||||
AnimatedFloatingSearchBox(
|
||||
listState = lazyListState,
|
||||
searchQuery = state.searchQuery,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
placeholderText = stringResource(KMR.strings.action_search_for_source),
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(
|
||||
horizontal = MaterialTheme.padding.medium,
|
||||
vertical = MaterialTheme.padding.small,
|
||||
)
|
||||
.align(Alignment.TopCenter),
|
||||
)
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -23,6 +25,7 @@ import androidx.compose.material3.TextButton
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -82,24 +85,12 @@ fun SourcesScreen(
|
|||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
// KMK -->
|
||||
else -> Column(
|
||||
// Wrap around so we can use stickyHeader
|
||||
else -> Box(
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
) {
|
||||
AnimatedFloatingSearchBox(
|
||||
listState = lazyListState,
|
||||
searchQuery = state.searchQuery,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
placeholderText = stringResource(KMR.strings.action_search_for_source),
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = MaterialTheme.padding.medium,
|
||||
vertical = MaterialTheme.padding.small,
|
||||
),
|
||||
)
|
||||
|
||||
FastScrollLazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(top = 65.dp),
|
||||
// KMK <--
|
||||
) {
|
||||
items(
|
||||
|
|
@ -141,6 +132,22 @@ fun SourcesScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
AnimatedFloatingSearchBox(
|
||||
listState = lazyListState,
|
||||
searchQuery = state.searchQuery,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
placeholderText = stringResource(KMR.strings.action_search_for_source),
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(
|
||||
horizontal = MaterialTheme.padding.medium,
|
||||
vertical = MaterialTheme.padding.small,
|
||||
)
|
||||
.align(Alignment.TopCenter),
|
||||
)
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ import tachiyomi.i18n.MR
|
|||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
|
||||
import tachiyomi.presentation.core.util.isScrolledToStart
|
||||
import tachiyomi.presentation.core.util.isScrollingDown
|
||||
import tachiyomi.presentation.core.util.isItemScrollingUp
|
||||
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
|
||||
|
|
@ -61,7 +60,7 @@ fun AnimatedFloatingSearchBox(
|
|||
keyboardController: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = listState.isScrolledToStart() || !listState.isScrollingDown(),
|
||||
visible = listState.isItemScrollingUp(),
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically(),
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,15 @@ package tachiyomi.presentation.core.util
|
|||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
@Composable
|
||||
fun LazyListState.shouldExpandFAB(): Boolean {
|
||||
|
|
@ -75,3 +79,37 @@ fun LazyListState.isScrollingDown(): Boolean {
|
|||
}
|
||||
}.value
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
@Composable
|
||||
private fun LazyListState.isItemScrolling(
|
||||
initialValue: Boolean,
|
||||
comparison: (Int, Int) -> Boolean,
|
||||
): Boolean {
|
||||
var isScrolling by remember { mutableStateOf(initialValue) }
|
||||
var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) }
|
||||
|
||||
LaunchedEffect(this) {
|
||||
snapshotFlow { firstVisibleItemIndex }
|
||||
.distinctUntilChanged()
|
||||
.collect { currentIndex ->
|
||||
if (previousIndex != currentIndex) {
|
||||
isScrolling = comparison(previousIndex, currentIndex)
|
||||
previousIndex = currentIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isScrolling
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LazyListState.isItemScrollingUp(initiallyVisible: Boolean = true): Boolean {
|
||||
return isItemScrolling(initialValue = initiallyVisible) { previous, current -> previous > current }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LazyListState.isItemScrollingDown(initiallyVisible: Boolean = false): Boolean {
|
||||
return isItemScrolling(initialValue = initiallyVisible) { previous, current -> previous < current }
|
||||
}
|
||||
// KMK <--
|
||||
|
|
|
|||
Loading…
Reference in a new issue