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

(cherry picked from commit 78aa50bb350b0142a3e0407b3d2f6084b9c1a835)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt
This commit is contained in:
LooKeR 2023-10-07 03:54:43 +05:30 committed by Jobobby04
parent c2488c41a2
commit 1aa2ad1e9c
2 changed files with 69 additions and 33 deletions

View File

@ -335,8 +335,14 @@ private fun MangaScreenSmallImpl(
val metadataDescription = metadataDescription(state.source) val metadataDescription = metadataDescription(state.source)
// SY <-- // SY <--
val isAnySelected by remember {
derivedStateOf {
chapters.fastAny { it.selected }
}
}
val internalOnBackPressed = { val internalOnBackPressed = {
if (chapters.fastAny { it.selected }) { if (isAnySelected) {
onAllChapterSelected(false) onAllChapterSelected(false)
} else { } else {
onBackClicked() onBackClicked()
@ -346,17 +352,22 @@ private fun MangaScreenSmallImpl(
Scaffold( Scaffold(
topBar = { topBar = {
val firstVisibleItemIndex by remember { val selectedChapterCount: Int = remember(chapters) {
derivedStateOf { chapterListState.firstVisibleItemIndex } chapters.count { it.selected }
} }
val firstVisibleItemScrollOffset by remember { val isFirstItemVisible by remember {
derivedStateOf { chapterListState.firstVisibleItemScrollOffset } derivedStateOf { chapterListState.firstVisibleItemIndex == 0 }
}
val isFirstItemScrolled by remember {
derivedStateOf { chapterListState.firstVisibleItemScrollOffset > 0 }
} }
val animatedTitleAlpha by animateFloatAsState( val animatedTitleAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0) 1f else 0f, if (!isFirstItemVisible) 1f else 0f,
label = "Top Bar Title",
) )
val animatedBgAlpha by animateFloatAsState( val animatedBgAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f, if (!isFirstItemVisible || isFirstItemScrolled) 1f else 0f,
label = "Top Bar Background",
) )
MangaToolbar( MangaToolbar(
title = state.manga.title, title = state.manga.title,
@ -376,14 +387,17 @@ private fun MangaScreenSmallImpl(
onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID }, onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID },
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow }, onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
// SY <-- // SY <--
actionModeCounter = chapters.count { it.selected }, actionModeCounter = selectedChapterCount,
onSelectAll = { onAllChapterSelected(true) }, onSelectAll = { onAllChapterSelected(true) },
onInvertSelection = { onInvertSelection() }, onInvertSelection = { onInvertSelection() },
) )
}, },
bottomBar = { bottomBar = {
val selectedChapters = remember(chapters) {
chapters.filter { it.selected }
}
SharedMangaBottomActionMenu( SharedMangaBottomActionMenu(
selected = chapters.filter { it.selected }, selected = selectedChapters,
onMultiBookmarkClicked = onMultiBookmarkClicked, onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked, onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked, onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
@ -394,19 +408,20 @@ private fun MangaScreenSmallImpl(
}, },
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
floatingActionButton = { floatingActionButton = {
val isFABVisible = remember(chapters) {
chapters.fastAny { !it.chapter.read } && !isAnySelected
}
AnimatedVisibility( AnimatedVisibility(
visible = chapters.fastAny { !it.chapter.read } && chapters.fastAll { !it.selected }, visible = isFABVisible,
enter = fadeIn(), enter = fadeIn(),
exit = fadeOut(), exit = fadeOut(),
) { ) {
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
text = { text = {
val id = if (state.chapters.fastAny { it.chapter.read }) { val isReading = remember(state.chapters) {
R.string.action_resume state.chapters.fastAny { it.chapter.read }
} else {
R.string.action_start
} }
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) }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading, onClick = onContinueReading,
@ -420,7 +435,7 @@ private fun MangaScreenSmallImpl(
PullRefresh( PullRefresh(
refreshing = state.isRefreshingData, refreshing = state.isRefreshingData,
onRefresh = onRefresh, onRefresh = onRefresh,
enabled = chapters.fastAll { !it.selected }, enabled = !isAnySelected,
indicatorPadding = WindowInsets.systemBars.only(WindowInsetsSides.Top).asPaddingValues(), indicatorPadding = WindowInsets.systemBars.only(WindowInsetsSides.Top).asPaddingValues(),
) { ) {
val layoutDirection = LocalLayoutDirection.current val layoutDirection = LocalLayoutDirection.current
@ -546,10 +561,13 @@ private fun MangaScreenSmallImpl(
key = MangaScreenItem.CHAPTER_HEADER, key = MangaScreenItem.CHAPTER_HEADER,
contentType = MangaScreenItem.CHAPTER_HEADER, contentType = MangaScreenItem.CHAPTER_HEADER,
) { ) {
val missingChapterCount = remember(chapters) {
chapters.map { it.chapter.chapterNumber }.missingChaptersCount()
}
ChapterHeader( ChapterHeader(
enabled = chapters.fastAll { !it.selected }, enabled = !isAnySelected,
chapterCount = chapters.size, chapterCount = chapters.size,
missingChapterCount = chapters.map { it.chapter.chapterNumber }.missingChaptersCount(), missingChapterCount = missingChapterCount,
onClick = onFilterClicked, onClick = onFilterClicked,
) )
} }
@ -640,6 +658,12 @@ fun MangaScreenLargeImpl(
val chapters = remember(state) { state.processedChapters } val chapters = remember(state) { state.processedChapters }
val isAnySelected by remember {
derivedStateOf {
chapters.fastAny { it.selected }
}
}
// SY --> // SY -->
val metadataDescription = metadataDescription(state.source) val metadataDescription = metadataDescription(state.source)
// SY <-- // SY <--
@ -649,7 +673,7 @@ fun MangaScreenLargeImpl(
PullRefresh( PullRefresh(
refreshing = state.isRefreshingData, refreshing = state.isRefreshingData,
onRefresh = onRefresh, onRefresh = onRefresh,
enabled = chapters.fastAll { !it.selected }, enabled = !isAnySelected,
indicatorPadding = PaddingValues( indicatorPadding = PaddingValues(
start = insetPadding.calculateStartPadding(layoutDirection), start = insetPadding.calculateStartPadding(layoutDirection),
top = with(density) { topBarHeight.toDp() }, top = with(density) { topBarHeight.toDp() },
@ -659,7 +683,7 @@ fun MangaScreenLargeImpl(
val chapterListState = rememberLazyListState() val chapterListState = rememberLazyListState()
val internalOnBackPressed = { val internalOnBackPressed = {
if (chapters.fastAny { it.selected }) { if (isAnySelected) {
onAllChapterSelected(false) onAllChapterSelected(false)
} else { } else {
onBackClicked() onBackClicked()
@ -669,10 +693,13 @@ fun MangaScreenLargeImpl(
Scaffold( Scaffold(
topBar = { topBar = {
val selectedChapterCount = remember(chapters) {
chapters.count { it.selected }
}
MangaToolbar( MangaToolbar(
modifier = Modifier.onSizeChanged { topBarHeight = it.height }, modifier = Modifier.onSizeChanged { topBarHeight = it.height },
title = state.manga.title, title = state.manga.title,
titleAlphaProvider = { if (chapters.fastAny { it.selected }) 1f else 0f }, titleAlphaProvider = { if (isAnySelected) 1f else 0f },
backgroundAlphaProvider = { 1f }, backgroundAlphaProvider = { 1f },
hasFilters = state.manga.chaptersFiltered(), hasFilters = state.manga.chaptersFiltered(),
onBackClicked = internalOnBackPressed, onBackClicked = internalOnBackPressed,
@ -688,7 +715,7 @@ fun MangaScreenLargeImpl(
onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID }, onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID },
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow }, onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
// SY <-- // SY <--
actionModeCounter = chapters.count { it.selected }, actionModeCounter = selectedChapterCount,
onSelectAll = { onAllChapterSelected(true) }, onSelectAll = { onAllChapterSelected(true) },
onInvertSelection = { onInvertSelection() }, onInvertSelection = { onInvertSelection() },
) )
@ -698,8 +725,11 @@ fun MangaScreenLargeImpl(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.BottomEnd, contentAlignment = Alignment.BottomEnd,
) { ) {
val selectedChapters = remember(chapters) {
chapters.filter { it.selected }
}
SharedMangaBottomActionMenu( SharedMangaBottomActionMenu(
selected = chapters.filter { it.selected }, selected = selectedChapters,
onMultiBookmarkClicked = onMultiBookmarkClicked, onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked, onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked, onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
@ -711,19 +741,20 @@ fun MangaScreenLargeImpl(
}, },
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
floatingActionButton = { floatingActionButton = {
val isFABVisible = remember(chapters) {
chapters.fastAny { !it.chapter.read } && !isAnySelected
}
AnimatedVisibility( AnimatedVisibility(
visible = chapters.fastAny { !it.chapter.read } && chapters.fastAll { !it.selected }, visible = isFABVisible,
enter = fadeIn(), enter = fadeIn(),
exit = fadeOut(), exit = fadeOut(),
) { ) {
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
text = { text = {
val id = if (state.chapters.fastAny { it.chapter.read }) { val isReading = remember(state.chapters) {
R.string.action_resume state.chapters.fastAny { it.chapter.read }
} else {
R.string.action_start
} }
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) }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading, onClick = onContinueReading,
@ -828,10 +859,13 @@ fun MangaScreenLargeImpl(
key = MangaScreenItem.CHAPTER_HEADER, key = MangaScreenItem.CHAPTER_HEADER,
contentType = MangaScreenItem.CHAPTER_HEADER, contentType = MangaScreenItem.CHAPTER_HEADER,
) { ) {
val missingChapterCount = remember(chapters) {
chapters.map { it.chapter.chapterNumber }.missingChaptersCount()
}
ChapterHeader( ChapterHeader(
enabled = chapters.fastAll { !it.selected }, enabled = !isAnySelected,
chapterCount = chapters.size, chapterCount = chapters.size,
missingChapterCount = chapters.map { it.chapter.chapterNumber }.missingChaptersCount(), missingChapterCount = missingChapterCount,
onClick = onFilterButtonClicked, onClick = onFilterButtonClicked,
) )
} }

View File

@ -322,7 +322,7 @@ fun ExpandableMangaDescription(
) { ) {
tags.forEach { tags.forEach {
TagsChip( TagsChip(
modifier = Modifier.padding(vertical = 4.dp), modifier = DefaultTagChipModifier,
text = it, text = it,
onClick = { onClick = {
tagSelected = it tagSelected = it
@ -339,7 +339,7 @@ fun ExpandableMangaDescription(
) { ) {
items(items = tags) { items(items = tags) {
TagsChip( TagsChip(
modifier = Modifier.padding(vertical = 4.dp), modifier = DefaultTagChipModifier,
text = it, text = it,
onClick = { onClick = {
tagSelected = it tagSelected = it
@ -691,6 +691,8 @@ private fun MangaSummary(
} }
} }
private val DefaultTagChipModifier = Modifier.padding(vertical = 4.dp)
@Composable @Composable
private fun RowScope.MangaActionButton( private fun RowScope.MangaActionButton(
title: String, title: String,