Skip to content

Commit

Permalink
Reduce recompositions caused by media selector updates, fix #1639
Browse files Browse the repository at this point in the history
  • Loading branch information
Him188 authored and StageGuard committed Feb 20, 2025
1 parent f335e38 commit d3bced1
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private fun PreviewEpisodeDetailsImpl(
)
},
mediaSelectorState = mediaSelectorState,
mediaSourceResultListPresentation = TestMediaSourceResultListPresentation,
mediaSourceResultListPresentation = { TestMediaSourceResultListPresentation },
authState = authState,
onSwitchEpisode = {},
onRefreshMediaSources = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import me.him188.ani.utils.platform.annotations.TestOnly
private fun PreviewEpisodePlayMediaSelectorSheet() = ProvideFoundationCompositionLocalsForPreview {
EpisodePlayMediaSelector(
rememberTestMediaSelectorState(),
TestMediaSourceResultListPresentation,
{ TestMediaSourceResultListPresentation },
onDismissRequest = {},
onRefresh = {},
onRestartSource = {},
Expand All @@ -39,7 +39,7 @@ private fun PreviewEpisodePlayMediaSelectorSheet() = ProvideFoundationCompositio
private fun PreviewEpisodePlayMediaSelectorSheet2() = ProvideFoundationCompositionLocalsForPreview {
EpisodePlayMediaSelector(
rememberTestMediaSelectorState(),
TestMediaSourceResultListPresentation,
{ TestMediaSourceResultListPresentation },
onDismissRequest = {},
onRefresh = {},
onRestartSource = {},
Expand All @@ -52,7 +52,7 @@ private fun PreviewEpisodePlayMediaSelectorSheet2() = ProvideFoundationCompositi
private fun PreviewEpisodePlayMediaSelectorSheet3() = ProvideFoundationCompositionLocalsForPreview {
EpisodePlayMediaSelector(
rememberTestMediaSelectorState(),
TestMediaSourceResultListPresentation,
{ TestMediaSourceResultListPresentation },
onDismissRequest = {},
onRefresh = {},
onRestartSource = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ private fun EpisodeSceneTabletVeryWide(
page.danmakuStatistics,
vm.videoStatisticsFlow,
page.mediaSelectorState,
page.mediaSourceResultListPresentation,
{ page.mediaSourceResultListPresentation },
vm.authState,
onSwitchEpisode = { episodeId ->
if (!vm.episodeSelectorState.selectEpisodeId(episodeId)) {
Expand Down Expand Up @@ -513,7 +513,7 @@ private fun EpisodeSceneContentPhone(
page.danmakuStatistics,
vm.videoStatisticsFlow,
page.mediaSelectorState,
page.mediaSourceResultListPresentation,
{ page.mediaSourceResultListPresentation },
vm.authState,
onSwitchEpisode = { episodeId ->
if (!vm.episodeSelectorState.selectEpisodeId(episodeId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,19 @@ class EpisodeViewModel(
).distinctUntilChanged(),
settingsRepository.danmakuEnabled.flow,
settingsRepository.danmakuConfig.flow,
mediaSourceResultsFlow,
) { subjectEpisodeBundle, loadError, fetchSelect, danmakuStatistics, danmakuEnabled, danmakuConfig, mediaSourceResultsPresentation ->
episodeSession.fetchSelectFlow.map { fetchSelect ->
if (fetchSelect != null) MediaSelectorState(
fetchSelect.mediaSelector,
mediaSourceInfoProvider,
backgroundScope,
) else {
// TODO: 2025/1/22 We should not use createTestMediaSelectorState
@OptIn(TestOnly::class)
createTestMediaSelectorState(backgroundScope)
}
},
mediaSourceResultsFlow.map { MediaSourceResultListPresentation(it) },
) { subjectEpisodeBundle, loadError, fetchSelect, danmakuStatistics, danmakuEnabled, danmakuConfig, mediaSelectorState, mediaSourceResultsPresentation ->

val (subject, episode) = if (subjectEpisodeBundle == null) {
SubjectPresentation.Placeholder to EpisodePresentation.Placeholder
Expand All @@ -576,16 +587,8 @@ class EpisodeViewModel(
}

EpisodePageState(
mediaSelectorState = if (fetchSelect != null) MediaSelectorState(
fetchSelect.mediaSelector,
mediaSourceInfoProvider,
backgroundScope,
) else {
// TODO: 2025/1/22 We should not use createTestMediaSelectorState
@OptIn(TestOnly::class)
createTestMediaSelectorState(backgroundScope)
},
mediaSourceResultListPresentation = MediaSourceResultListPresentation(mediaSourceResultsPresentation),
mediaSelectorState = mediaSelectorState,
mediaSourceResultListPresentation = mediaSourceResultsPresentation,
danmakuStatistics = danmakuStatistics,
subjectPresentation = subject,
episodePresentation = episode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ fun EpisodeDetails(
danmakuStatistics: DanmakuStatistics,
videoStatisticsFlow: Flow<VideoStatistics>,
mediaSelectorState: MediaSelectorState,
mediaSourceResultListPresentation: MediaSourceResultListPresentation,
mediaSourceResultListPresentation: () -> MediaSourceResultListPresentation,
authState: AuthState,
onSwitchEpisode: (episodeId: Int) -> Unit,
onRefreshMediaSources: () -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import me.him188.ani.datasources.api.Media
@Composable
fun EpisodePlayMediaSelector(
mediaSelector: MediaSelectorState,
sourceResults: MediaSourceResultListPresentation,
sourceResults: () -> MediaSourceResultListPresentation,
onDismissRequest: () -> Unit,
onRefresh: () -> Unit,
onRestartSource: (MediaSourceResultPresentation) -> Unit,
Expand All @@ -43,7 +43,7 @@ fun EpisodePlayMediaSelector(
mediaSelector,
sourceResults = {
MediaSourceResultsView(
sourceResults, mediaSelector,
sourceResults(), mediaSelector,
onRefresh = onRefresh,
onRestartSource = onRestartSource,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package me.him188.ani.app.ui.subject.episode.mediaFetch

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -114,11 +115,13 @@ fun <T : Any> MediaPreferenceItemState<T>.preferOrRemove(value: T?): Job {
/**
* Wraps [MediaSelector] to provide states for UI.
*/
@Stable
class MediaSelectorState(
private val mediaSelector: MediaSelector,
val mediaSourceInfoProvider: MediaSourceInfoProvider,
private val backgroundScope: CoroutineScope,
) {
@Immutable
data class Presentation(
val filteredCandidates: List<MaybeExcludedMedia>,
val preferredCandidates: List<Media>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
* Copyright (C) 2024-2025 OpenAni and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
Expand Down Expand Up @@ -69,8 +69,13 @@ fun MediaSelectorView(
stickyHeaderBackgroundColor: Color = Color.Unspecified,
itemProgressBar: @Composable RowScope.(MediaGroup) -> Unit = { group ->
val presentation by state.presentationFlow.collectAsStateWithLifecycle()
val showIndicator by remember(group) {
derivedStateOf {
group.list.any { it.original === presentation.selected }
}
}
FastLinearProgressIndicator(
group.list.any { it.original === presentation.selected },
showIndicator,
Modifier.fillMaxWidth().padding(horizontal = 4.dp),
delayMillis = 300,
)
Expand Down

0 comments on commit d3bced1

Please # to comment.