From 1aa2ad1e9c60194123025b15bf72301bc9d75d72 Mon Sep 17 00:00:00 2001 From: LooKeR Date: Sat, 7 Oct 2023 03:54:43 +0530 Subject: [PATCH] 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 --- .../kanade/presentation/manga/MangaScreen.kt | 96 +++++++++++++------ .../manga/components/MangaInfoHeader.kt | 6 +- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index b1aab775a..887f989d5 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -335,8 +335,14 @@ private fun MangaScreenSmallImpl( val metadataDescription = metadataDescription(state.source) // SY <-- + val isAnySelected by remember { + derivedStateOf { + chapters.fastAny { it.selected } + } + } + val internalOnBackPressed = { - if (chapters.fastAny { it.selected }) { + if (isAnySelected) { onAllChapterSelected(false) } else { onBackClicked() @@ -346,17 +352,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, @@ -376,14 +387,17 @@ private fun MangaScreenSmallImpl( onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID }, onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow }, // SY <-- - 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, @@ -394,19 +408,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, @@ -420,7 +435,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 @@ -546,10 +561,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, ) } @@ -640,6 +658,12 @@ fun MangaScreenLargeImpl( val chapters = remember(state) { state.processedChapters } + val isAnySelected by remember { + derivedStateOf { + chapters.fastAny { it.selected } + } + } + // SY --> val metadataDescription = metadataDescription(state.source) // SY <-- @@ -649,7 +673,7 @@ fun MangaScreenLargeImpl( PullRefresh( refreshing = state.isRefreshingData, onRefresh = onRefresh, - enabled = chapters.fastAll { !it.selected }, + enabled = !isAnySelected, indicatorPadding = PaddingValues( start = insetPadding.calculateStartPadding(layoutDirection), top = with(density) { topBarHeight.toDp() }, @@ -659,7 +683,7 @@ fun MangaScreenLargeImpl( val chapterListState = rememberLazyListState() val internalOnBackPressed = { - if (chapters.fastAny { it.selected }) { + if (isAnySelected) { onAllChapterSelected(false) } else { onBackClicked() @@ -669,10 +693,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, @@ -688,7 +715,7 @@ fun MangaScreenLargeImpl( onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID }, onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow }, // SY <-- - actionModeCounter = chapters.count { it.selected }, + actionModeCounter = selectedChapterCount, onSelectAll = { onAllChapterSelected(true) }, onInvertSelection = { onInvertSelection() }, ) @@ -698,8 +725,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, @@ -711,19 +741,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, @@ -828,10 +859,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, ) } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 692edc129..f04c57e82 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -322,7 +322,7 @@ fun ExpandableMangaDescription( ) { tags.forEach { TagsChip( - modifier = Modifier.padding(vertical = 4.dp), + modifier = DefaultTagChipModifier, text = it, onClick = { tagSelected = it @@ -339,7 +339,7 @@ fun ExpandableMangaDescription( ) { items(items = tags) { TagsChip( - modifier = Modifier.padding(vertical = 4.dp), + modifier = DefaultTagChipModifier, text = it, onClick = { tagSelected = it @@ -691,6 +691,8 @@ private fun MangaSummary( } } +private val DefaultTagChipModifier = Modifier.padding(vertical = 4.dp) + @Composable private fun RowScope.MangaActionButton( title: String,