Reduce recomposition of MangaHeader (#9985)
* Reduce recomposition of MangaHeader * Reuse `Modifier` for `Tags` Reference: https://developer.android.com/jetpack/compose/modifiers#reusing-modifiers * Don't recalculate Read State on recomposition * Fix Linting issue * Optimize chapter state calculations
This commit is contained in:
parent
7f0f67d752
commit
78aa50bb35
2 changed files with 69 additions and 33 deletions
|
@ -268,8 +268,14 @@ private fun MangaScreenSmallImpl(
|
|||
|
||||
val chapters = remember(state) { state.processedChapters }
|
||||
|
||||
val isAnySelected by remember {
|
||||
derivedStateOf {
|
||||
chapters.fastAny { it.selected }
|
||||
}
|
||||
}
|
||||
|
||||
val internalOnBackPressed = {
|
||||
if (chapters.fastAny { it.selected }) {
|
||||
if (isAnySelected) {
|
||||
onAllChapterSelected(false)
|
||||
} else {
|
||||
onBackClicked()
|
||||
|
@ -279,17 +285,22 @@ private fun MangaScreenSmallImpl(
|
|||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
val firstVisibleItemIndex by remember {
|
||||
derivedStateOf { chapterListState.firstVisibleItemIndex }
|
||||
val selectedChapterCount: Int = remember(chapters) {
|
||||
chapters.count { it.selected }
|
||||
}
|
||||
val firstVisibleItemScrollOffset by remember {
|
||||
derivedStateOf { chapterListState.firstVisibleItemScrollOffset }
|
||||
val isFirstItemVisible by remember {
|
||||
derivedStateOf { chapterListState.firstVisibleItemIndex == 0 }
|
||||
}
|
||||
val isFirstItemScrolled by remember {
|
||||
derivedStateOf { chapterListState.firstVisibleItemScrollOffset > 0 }
|
||||
}
|
||||
val animatedTitleAlpha by animateFloatAsState(
|
||||
if (firstVisibleItemIndex > 0) 1f else 0f,
|
||||
if (!isFirstItemVisible) 1f else 0f,
|
||||
label = "Top Bar Title",
|
||||
)
|
||||
val animatedBgAlpha by animateFloatAsState(
|
||||
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f,
|
||||
if (!isFirstItemVisible || isFirstItemScrolled) 1f else 0f,
|
||||
label = "Top Bar Background",
|
||||
)
|
||||
MangaToolbar(
|
||||
title = state.manga.title,
|
||||
|
@ -303,14 +314,17 @@ private fun MangaScreenSmallImpl(
|
|||
onClickEditCategory = onEditCategoryClicked,
|
||||
onClickRefresh = onRefresh,
|
||||
onClickMigrate = onMigrateClicked,
|
||||
actionModeCounter = chapters.count { it.selected },
|
||||
actionModeCounter = selectedChapterCount,
|
||||
onSelectAll = { onAllChapterSelected(true) },
|
||||
onInvertSelection = { onInvertSelection() },
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
val selectedChapters = remember(chapters) {
|
||||
chapters.filter { it.selected }
|
||||
}
|
||||
SharedMangaBottomActionMenu(
|
||||
selected = chapters.filter { it.selected },
|
||||
selected = selectedChapters,
|
||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||
|
@ -321,19 +335,20 @@ private fun MangaScreenSmallImpl(
|
|||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
floatingActionButton = {
|
||||
val isFABVisible = remember(chapters) {
|
||||
chapters.fastAny { !it.chapter.read } && !isAnySelected
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = chapters.fastAny { !it.chapter.read } && chapters.fastAll { !it.selected },
|
||||
visible = isFABVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (state.chapters.fastAny { it.chapter.read }) {
|
||||
R.string.action_resume
|
||||
} else {
|
||||
R.string.action_start
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(text = stringResource(id))
|
||||
Text(text = stringResource(if (isReading) R.string.action_resume else R.string.action_start))
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
|
@ -347,7 +362,7 @@ private fun MangaScreenSmallImpl(
|
|||
PullRefresh(
|
||||
refreshing = state.isRefreshingData,
|
||||
onRefresh = onRefresh,
|
||||
enabled = chapters.fastAll { !it.selected },
|
||||
enabled = !isAnySelected,
|
||||
indicatorPadding = WindowInsets.systemBars.only(WindowInsetsSides.Top).asPaddingValues(),
|
||||
) {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
@ -419,10 +434,13 @@ private fun MangaScreenSmallImpl(
|
|||
key = MangaScreenItem.CHAPTER_HEADER,
|
||||
contentType = MangaScreenItem.CHAPTER_HEADER,
|
||||
) {
|
||||
val missingChapterCount = remember(chapters) {
|
||||
chapters.map { it.chapter.chapterNumber }.missingChaptersCount()
|
||||
}
|
||||
ChapterHeader(
|
||||
enabled = chapters.fastAll { !it.selected },
|
||||
enabled = !isAnySelected,
|
||||
chapterCount = chapters.size,
|
||||
missingChapterCount = chapters.map { it.chapter.chapterNumber }.missingChaptersCount(),
|
||||
missingChapterCount = missingChapterCount,
|
||||
onClick = onFilterClicked,
|
||||
)
|
||||
}
|
||||
|
@ -500,12 +518,18 @@ fun MangaScreenLargeImpl(
|
|||
|
||||
val chapters = remember(state) { state.processedChapters }
|
||||
|
||||
val isAnySelected by remember {
|
||||
derivedStateOf {
|
||||
chapters.fastAny { it.selected }
|
||||
}
|
||||
}
|
||||
|
||||
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
||||
var topBarHeight by remember { mutableIntStateOf(0) }
|
||||
PullRefresh(
|
||||
refreshing = state.isRefreshingData,
|
||||
onRefresh = onRefresh,
|
||||
enabled = chapters.fastAll { !it.selected },
|
||||
enabled = !isAnySelected,
|
||||
indicatorPadding = PaddingValues(
|
||||
start = insetPadding.calculateStartPadding(layoutDirection),
|
||||
top = with(density) { topBarHeight.toDp() },
|
||||
|
@ -515,7 +539,7 @@ fun MangaScreenLargeImpl(
|
|||
val chapterListState = rememberLazyListState()
|
||||
|
||||
val internalOnBackPressed = {
|
||||
if (chapters.fastAny { it.selected }) {
|
||||
if (isAnySelected) {
|
||||
onAllChapterSelected(false)
|
||||
} else {
|
||||
onBackClicked()
|
||||
|
@ -525,10 +549,13 @@ fun MangaScreenLargeImpl(
|
|||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
val selectedChapterCount = remember(chapters) {
|
||||
chapters.count { it.selected }
|
||||
}
|
||||
MangaToolbar(
|
||||
modifier = Modifier.onSizeChanged { topBarHeight = it.height },
|
||||
title = state.manga.title,
|
||||
titleAlphaProvider = { if (chapters.fastAny { it.selected }) 1f else 0f },
|
||||
titleAlphaProvider = { if (isAnySelected) 1f else 0f },
|
||||
backgroundAlphaProvider = { 1f },
|
||||
hasFilters = state.manga.chaptersFiltered(),
|
||||
onBackClicked = internalOnBackPressed,
|
||||
|
@ -538,7 +565,7 @@ fun MangaScreenLargeImpl(
|
|||
onClickEditCategory = onEditCategoryClicked,
|
||||
onClickRefresh = onRefresh,
|
||||
onClickMigrate = onMigrateClicked,
|
||||
actionModeCounter = chapters.count { it.selected },
|
||||
actionModeCounter = selectedChapterCount,
|
||||
onSelectAll = { onAllChapterSelected(true) },
|
||||
onInvertSelection = { onInvertSelection() },
|
||||
)
|
||||
|
@ -548,8 +575,11 @@ fun MangaScreenLargeImpl(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.BottomEnd,
|
||||
) {
|
||||
val selectedChapters = remember(chapters) {
|
||||
chapters.filter { it.selected }
|
||||
}
|
||||
SharedMangaBottomActionMenu(
|
||||
selected = chapters.filter { it.selected },
|
||||
selected = selectedChapters,
|
||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||
|
@ -561,19 +591,20 @@ fun MangaScreenLargeImpl(
|
|||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
floatingActionButton = {
|
||||
val isFABVisible = remember(chapters) {
|
||||
chapters.fastAny { !it.chapter.read } && !isAnySelected
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = chapters.fastAny { !it.chapter.read } && chapters.fastAll { !it.selected },
|
||||
visible = isFABVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (state.chapters.fastAny { it.chapter.read }) {
|
||||
R.string.action_resume
|
||||
} else {
|
||||
R.string.action_start
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(text = stringResource(id))
|
||||
Text(text = stringResource(if (isReading) R.string.action_resume else R.string.action_start))
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
|
@ -644,10 +675,13 @@ fun MangaScreenLargeImpl(
|
|||
key = MangaScreenItem.CHAPTER_HEADER,
|
||||
contentType = MangaScreenItem.CHAPTER_HEADER,
|
||||
) {
|
||||
val missingChapterCount = remember(chapters) {
|
||||
chapters.map { it.chapter.chapterNumber }.missingChaptersCount()
|
||||
}
|
||||
ChapterHeader(
|
||||
enabled = chapters.fastAll { !it.selected },
|
||||
enabled = !isAnySelected,
|
||||
chapterCount = chapters.size,
|
||||
missingChapterCount = chapters.map { it.chapter.chapterNumber }.missingChaptersCount(),
|
||||
missingChapterCount = missingChapterCount,
|
||||
onClick = onFilterButtonClicked,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ fun ExpandableMangaDescription(
|
|||
) {
|
||||
tags.forEach {
|
||||
TagsChip(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
modifier = DefaultTagChipModifier,
|
||||
text = it,
|
||||
onClick = {
|
||||
tagSelected = it
|
||||
|
@ -302,7 +302,7 @@ fun ExpandableMangaDescription(
|
|||
) {
|
||||
items(items = tags) {
|
||||
TagsChip(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
modifier = DefaultTagChipModifier,
|
||||
text = it,
|
||||
onClick = {
|
||||
tagSelected = it
|
||||
|
@ -654,6 +654,8 @@ private fun MangaSummary(
|
|||
}
|
||||
}
|
||||
|
||||
private val DefaultTagChipModifier = Modifier.padding(vertical = 4.dp)
|
||||
|
||||
@Composable
|
||||
private fun TagsChip(
|
||||
text: String,
|
||||
|
|
Reference in a new issue