Fix disappearance items when fast scrolling (#1035)

* Don't use animateItem's fade-in/fade-out in FastScrollLazyColumn

* Move to extension function

Avoid using animateItemPlacement name since it's shadowed by compose-bom's deprecated one

(cherry picked from commit 913ff22132390a59a13c463645ce954c7cbc5c6b)
This commit is contained in:
Tran M. Cuong 2024-07-27 02:01:32 +07:00 committed by Jobobby04
parent 365cd0b14d
commit 3408ef635d
12 changed files with 34 additions and 20 deletions

View File

@ -48,6 +48,7 @@ import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.extension.model.InstallStep
@ -188,14 +189,14 @@ private fun ExtensionContent(
} }
ExtensionHeader( ExtensionHeader(
textRes = header.textRes, textRes = header.textRes,
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
action = action, action = action,
) )
} }
is ExtensionUiModel.Header.Text -> { is ExtensionUiModel.Header.Text -> {
ExtensionHeader( ExtensionHeader(
text = header.text, text = header.text,
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
) )
} }
} }
@ -213,7 +214,7 @@ private fun ExtensionContent(
}, },
) { item -> ) { item ->
ExtensionItem( ExtensionItem(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
item = item, item = item,
onClickItem = { onClickItem = {
when (it) { when (it) {

View File

@ -92,7 +92,7 @@ fun FeedScreen(
refreshing = true refreshing = true
onRefresh() onRefresh()
}, },
enabled = { !state.isLoadingItems }, enabled = !state.isLoadingItems,
) { ) {
ScrollbarLazyColumn( ScrollbarLazyColumn(
contentPadding = contentPadding + topSmallPaddingValues, contentPadding = contentPadding + topSmallPaddingValues,
@ -103,7 +103,6 @@ fun FeedScreen(
key = { it.feed.id }, key = { it.feed.id },
) { item -> ) { item ->
GlobalSearchResultItem( GlobalSearchResultItem(
modifier = Modifier.animateItemPlacement(),
title = item.title, title = item.title,
subtitle = item.subtitle, subtitle = item.subtitle,
onLongClick = { onLongClick = {
@ -116,6 +115,7 @@ fun FeedScreen(
onClickSource(item.source) onClickSource(item.source)
} }
}, },
modifier = Modifier.animateItem(),
) { ) {
FeedItem( FeedItem(
item = item, item = item,

View File

@ -28,6 +28,7 @@ import eu.kanade.presentation.browse.components.MigrationItem
import eu.kanade.presentation.browse.components.MigrationItemResult import eu.kanade.presentation.browse.components.MigrationItemResult
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -95,7 +96,7 @@ fun MigrationListScreen(
Row( Row(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.animateItemPlacement() .animateItemFastScroll()
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.height(IntrinsicSize.Min), .height(IntrinsicSize.Min),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,

View File

@ -15,6 +15,7 @@ import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.components.AppBarTitle import eu.kanade.presentation.components.AppBarTitle
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.util.animateItemFastScroll
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.model.FeedSavedSearch import tachiyomi.domain.source.model.FeedSavedSearch
@ -153,7 +154,7 @@ fun SourceFeedList(
key = { it.id }, key = { it.id },
) { item -> ) { item ->
GlobalSearchResultItem( GlobalSearchResultItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemFastScroll(),
title = item.title, title = item.title,
subtitle = null, subtitle = null,
onLongClick = if (item is SourceFeedUI.SourceSavedSearch) { onLongClick = if (item is SourceFeedUI.SourceSavedSearch) {

View File

@ -11,6 +11,7 @@ import androidx.compose.ui.platform.LocalContext
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
@ -79,7 +80,7 @@ private fun SourcesFilterContent(
contentType = "source-filter-header", contentType = "source-filter-header",
) { ) {
SourcesFilterHeader( SourcesFilterHeader(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
language = language, language = language,
enabled = enabled, enabled = enabled,
onClickItem = onClickLanguage, onClickItem = onClickLanguage,
@ -95,7 +96,7 @@ private fun SourcesFilterContent(
sources.none { it.id.toString() in state.disabledSources } sources.none { it.id.toString() in state.disabledSources }
} }
SourcesFilterToggle( SourcesFilterToggle(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItem(),
isEnabled = toggleEnabled, isEnabled = toggleEnabled,
onClickItem = { onClickItem = {
onClickSources(!toggleEnabled, sources) onClickSources(!toggleEnabled, sources)
@ -109,7 +110,7 @@ private fun SourcesFilterContent(
contentType = { "source-filter-item" }, contentType = { "source-filter-item" },
) { source -> ) { source ->
SourcesFilterItem( SourcesFilterItem(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
source = source, source = source,
enabled = "${source.id}" !in state.disabledSources, enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource, onClickItem = onClickSource,

View File

@ -26,7 +26,7 @@ fun BiometricTimesContent(
) { ) {
items(timeRanges, key = { it.formattedString }) { timeRange -> items(timeRanges, key = { it.formattedString }) { timeRange ->
BiometricTimesListItem( BiometricTimesListItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItem(),
timeRange = timeRange, timeRange = timeRange,
onDelete = { onClickDelete(timeRange) }, onDelete = { onClickDelete(timeRange) },
) )

View File

@ -27,7 +27,7 @@ fun SortTagContent(
) { ) {
itemsIndexed(tags, key = { _, tag -> tag }) { index, tag -> itemsIndexed(tags, key = { _, tag -> tag }) { index, tag ->
SortTagListItem( SortTagListItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItem(),
tag = tag, tag = tag,
canMoveUp = index != 0, canMoveUp = index != 0,
canMoveDown = index != tags.lastIndex, canMoveDown = index != tags.lastIndex,

View File

@ -26,7 +26,7 @@ fun SourceCategoryContent(
) { ) {
items(categories, key = { it }) { category -> items(categories, key = { it }) { category ->
SourceCategoryListItem( SourceCategoryListItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItem(),
category = category, category = category,
onRename = { onClickRename(category) }, onRename = { onClickRename(category) },
onDelete = { onClickDelete(category) }, onDelete = { onClickDelete(category) },

View File

@ -18,6 +18,7 @@ import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.components.relativeDateText import eu.kanade.presentation.components.relativeDateText
import eu.kanade.presentation.history.components.HistoryItem import eu.kanade.presentation.history.components.HistoryItem
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -114,14 +115,14 @@ private fun HistoryScreenContent(
when (item) { when (item) {
is HistoryUiModel.Header -> { is HistoryUiModel.Header -> {
ListGroupHeader( ListGroupHeader(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
text = relativeDateText(item.date), text = relativeDateText(item.date),
) )
} }
is HistoryUiModel.Item -> { is HistoryUiModel.Item -> {
val value = item.item val value = item.item
HistoryItem( HistoryItem(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
history = value, history = value,
onClickCover = { onClickCover(value) }, onClickCover = { onClickCover(value) },
onClickResume = { onClickResume(value) }, onClickResume = { onClickResume(value) },

View File

@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -141,7 +141,7 @@ fun TagsChip(
border: ChipBorder? = SuggestionChipDefaults.suggestionChipBorder(), border: ChipBorder? = SuggestionChipDefaults.suggestionChipBorder(),
borderM3: BorderStroke? = SuggestionChipDefaultsM3.suggestionChipBorder(enabled = true), borderM3: BorderStroke? = SuggestionChipDefaultsM3.suggestionChipBorder(enabled = true),
) { ) {
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) { CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
if (onClick != null) { if (onClick != null) {
SuggestionChip( SuggestionChip(
modifier = modifier, modifier = modifier,

View File

@ -37,6 +37,7 @@ import eu.kanade.presentation.manga.components.ChapterDownloadAction
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
import eu.kanade.presentation.manga.components.DotSeparatorText import eu.kanade.presentation.manga.components.DotSeparatorText
import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.manga.components.MangaCover
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem import eu.kanade.tachiyomi.ui.updates.UpdatesItem
@ -54,7 +55,7 @@ internal fun LazyListScope.updatesLastUpdatedItem(
item(key = "updates-lastUpdated") { item(key = "updates-lastUpdated") {
Box( Box(
modifier = Modifier modifier = Modifier
.animateItem() .animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small), .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
) { ) {
Text( Text(
@ -94,14 +95,14 @@ internal fun LazyListScope.updatesUiItems(
when (item) { when (item) {
is UpdatesUiModel.Header -> { is UpdatesUiModel.Header -> {
ListGroupHeader( ListGroupHeader(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
text = relativeDateText(item.date), text = relativeDateText(item.date),
) )
} }
is UpdatesUiModel.Item -> { is UpdatesUiModel.Item -> {
val updatesItem = item.item val updatesItem = item.item
UpdatesUiItem( UpdatesUiItem(
modifier = Modifier.animateItem(), modifier = Modifier.animateItemFastScroll(),
update = updatesItem.update, update = updatesItem.update,
selected = updatesItem.selected, selected = updatesItem.selected,
readProgress = updatesItem.update.lastPageRead readProgress = updatesItem.update.lastPageRead

View File

@ -0,0 +1,8 @@
package eu.kanade.presentation.util
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.ui.Modifier
// https://issuetracker.google.com/352584409
context(LazyItemScope)
fun Modifier.animateItemFastScroll() = this.animateItem(fadeInSpec = null, fadeOutSpec = null)