Skip to content

Commit f224691

Browse files
authored
Merge pull request #134 from kalzEOS/feat/root-screen-recomposition-split
Ship v3.3.4: narrow RootScreen recomposition scope
2 parents d22e614 + ab3fad3 commit f224691

5 files changed

Lines changed: 194 additions & 161 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import java.util.Properties
33
import org.gradle.api.provider.Property
44
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
55

6-
val appVersionCode = 130
7-
val appVersionName = "3.3.3"
6+
val appVersionCode = 131
7+
val appVersionName = "3.3.4"
88

99
plugins {
1010
alias(libs.plugins.android.application)

app/src/main/java/com/example/xtreamplayer/BrowseScreen.kt

Lines changed: 100 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,35 @@ import androidx.compose.ui.platform.LocalDensity
3838
import androidx.compose.ui.platform.LocalFocusManager
3939
import androidx.compose.ui.unit.dp
4040
import androidx.compose.ui.unit.sp
41+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4142
import com.example.xtreamplayer.auth.AuthConfig
4243
import com.example.xtreamplayer.auth.AuthUiState
4344
import com.example.xtreamplayer.content.CategoryItem
4445
import com.example.xtreamplayer.content.ContentItem
4546
import com.example.xtreamplayer.content.ContentRepository
4647
import com.example.xtreamplayer.content.ContentType
47-
import com.example.xtreamplayer.content.ContinueWatchingEntry
4848
import com.example.xtreamplayer.content.ContinueWatchingRepository
4949
import com.example.xtreamplayer.content.FavoritesRepository
50-
import com.example.xtreamplayer.content.HistoryRepository
5150
import com.example.xtreamplayer.content.ProgressiveSyncCoordinator
5251
import com.example.xtreamplayer.content.ProgressiveSyncState
5352
import com.example.xtreamplayer.content.SubtitleRepository
5453
import com.example.xtreamplayer.settings.SettingsState
5554
import com.example.xtreamplayer.settings.SettingsViewModel
55+
import com.example.xtreamplayer.ui.components.NAV_WIDTH
5656
import com.example.xtreamplayer.ui.components.SideNav
5757
import com.example.xtreamplayer.ui.theme.AppTheme
5858
import java.util.Locale
5959
import kotlinx.coroutines.CoroutineScope
6060
import kotlinx.coroutines.Dispatchers
6161
import kotlinx.coroutines.delay
62+
import kotlinx.coroutines.flow.flowOf
6263
import kotlinx.coroutines.launch
6364
import kotlinx.coroutines.withContext
6465
import timber.log.Timber
6566
import androidx.compose.runtime.withFrameNanos
6667

68+
private const val BROWSE_NAV_ANIM_DURATION_MS = 180
69+
6770
@Composable
6871
internal fun BrowseScreen(
6972
context: Context,
@@ -76,10 +79,6 @@ internal fun BrowseScreen(
7679
appVersionName: String,
7780
selectedSectionState: MutableState<Section>,
7881
navExpandedState: MutableState<Boolean>,
79-
navLayoutExpanded: Boolean,
80-
navSlideExpanded: Boolean,
81-
navOffsetPx: Float,
82-
navProgress: Float,
8382
moveFocusToNavState: MutableState<Boolean>,
8483
focusToContentTriggerState: MutableState<Int>,
8584
showManageListsState: MutableState<Boolean>,
@@ -97,7 +96,6 @@ internal fun BrowseScreen(
9796
cacheClearNonceState: MutableState<Int>,
9897
contentRepository: ContentRepository,
9998
favoritesRepository: FavoritesRepository,
100-
historyRepository: HistoryRepository,
10199
continueWatchingRepository: ContinueWatchingRepository,
102100
subtitleRepository: SubtitleRepository,
103101
playbackEngine: com.example.xtreamplayer.player.Media3PlaybackEngine,
@@ -117,11 +115,6 @@ internal fun BrowseScreen(
117115
contentItemFocusRequester: FocusRequester,
118116
resumeFocusId: String?,
119117
resumeFocusRequester: FocusRequester,
120-
filteredContinueWatchingItems: List<ContinueWatchingEntry>,
121-
filteredFavoriteContentItems: List<ContentItem>,
122-
filteredFavoriteCategoryItems: List<CategoryItem>,
123-
filteredFavoriteContentKeys: Set<String>,
124-
filteredFavoriteCategoryKeys: Set<String>,
125118
isPlaybackActive: Boolean,
126119
onItemFocused: (ContentItem) -> Unit,
127120
onPlay: (ContentItem, List<ContentItem>) -> Unit,
@@ -134,8 +127,6 @@ internal fun BrowseScreen(
134127
localResumePositionMsForUri: (Uri) -> Long?,
135128
onToggleFavorite: (ContentItem) -> Unit,
136129
onToggleCategoryFavorite: (CategoryItem) -> Unit,
137-
isItemFavorite: (ContentItem) -> Boolean,
138-
isCategoryFavorite: (CategoryItem) -> Boolean,
139130
onSeriesPlaybackStart: (ContentItem) -> Unit,
140131
onTriggerSectionSync: (Section, AuthConfig) -> Unit,
141132
onEditList: () -> Unit,
@@ -164,6 +155,101 @@ internal fun BrowseScreen(
164155
var cacheClearNonce by cacheClearNonceState
165156
val focusManager = LocalFocusManager.current
166157
var navMoveToContentTrigger by remember { mutableIntStateOf(0) }
158+
var navLayoutExpanded by remember { mutableStateOf(true) }
159+
var navSlideExpanded by remember { mutableStateOf(true) }
160+
LaunchedEffect(navExpanded) {
161+
if (navExpanded) {
162+
navLayoutExpanded = true
163+
navSlideExpanded = false
164+
withFrameNanos {}
165+
delay(16)
166+
navSlideExpanded = true
167+
} else {
168+
navSlideExpanded = false
169+
delay(BROWSE_NAV_ANIM_DURATION_MS.toLong())
170+
if (!navExpanded) {
171+
navLayoutExpanded = false
172+
}
173+
}
174+
}
175+
val navProgress by animateFloatAsState(
176+
targetValue = if (navSlideExpanded) 1f else 0f,
177+
animationSpec = tween(durationMillis = BROWSE_NAV_ANIM_DURATION_MS),
178+
label = "browseNavSlide"
179+
)
180+
val navWidthPx = with(LocalDensity.current) { NAV_WIDTH.toPx() }
181+
val navOffsetPx = -navWidthPx * (1f - navProgress)
182+
val favoriteContentKeys by
183+
favoritesRepository.favoriteContentKeys.collectAsStateWithLifecycle(initialValue = emptySet())
184+
val favoriteCategoryKeys by
185+
favoritesRepository.favoriteCategoryKeys.collectAsStateWithLifecycle(initialValue = emptySet())
186+
val favoriteContentEntries by
187+
favoritesRepository.favoriteContentEntries.collectAsStateWithLifecycle(initialValue = emptyList())
188+
val favoriteCategoryEntries by
189+
favoritesRepository.favoriteCategoryEntries.collectAsStateWithLifecycle(initialValue = emptyList())
190+
val filteredFavoriteContentKeys =
191+
remember(favoriteContentKeys, activeConfig) {
192+
if (activeConfig == null) {
193+
emptySet()
194+
} else {
195+
favoritesRepository.filterKeysForConfig(favoriteContentKeys, activeConfig)
196+
}
197+
}
198+
val filteredFavoriteCategoryKeys =
199+
remember(favoriteCategoryKeys, activeConfig) {
200+
if (activeConfig == null) {
201+
emptySet()
202+
} else {
203+
favoritesRepository.filterKeysForConfig(favoriteCategoryKeys, activeConfig)
204+
}
205+
}
206+
val filteredFavoriteContentItems =
207+
remember(favoriteContentEntries, filteredFavoriteContentKeys, activeConfig) {
208+
if (activeConfig == null) {
209+
emptyList()
210+
} else {
211+
favoriteContentEntries
212+
.filter {
213+
favoritesRepository.isKeyForConfig(it.key, activeConfig) &&
214+
filteredFavoriteContentKeys.contains(it.key)
215+
}
216+
.map { it.item }
217+
.distinctBy { "${it.contentType.name}:${it.id}" }
218+
}
219+
}
220+
val filteredFavoriteCategoryItems =
221+
remember(favoriteCategoryEntries, filteredFavoriteCategoryKeys, activeConfig) {
222+
if (activeConfig == null) {
223+
emptyList()
224+
} else {
225+
favoriteCategoryEntries
226+
.filter {
227+
favoritesRepository.isKeyForConfig(it.key, activeConfig) &&
228+
filteredFavoriteCategoryKeys.contains(it.key)
229+
}
230+
.map { it.category }
231+
.distinctBy { "${it.type.name}:${it.id}" }
232+
}
233+
}
234+
val isItemFavorite: (ContentItem) -> Boolean = { item ->
235+
val config = activeConfig
236+
config != null && favoritesRepository.isContentFavorite(favoriteContentKeys, config, item)
237+
}
238+
val isCategoryFavorite: (CategoryItem) -> Boolean = { category ->
239+
val config = activeConfig
240+
config != null && favoritesRepository.isCategoryFavorite(favoriteCategoryKeys, config, category)
241+
}
242+
val filteredContinueWatchingFlow =
243+
remember(activeConfig) {
244+
val config = activeConfig
245+
if (config == null) {
246+
flowOf(emptyList())
247+
} else {
248+
continueWatchingRepository.continueWatchingEntriesForConfig(config)
249+
}
250+
}
251+
val filteredContinueWatchingItems by
252+
filteredContinueWatchingFlow.collectAsStateWithLifecycle(initialValue = emptyList())
167253

168254
LaunchedEffect(navMoveToContentTrigger) {
169255
if (navMoveToContentTrigger <= 0) return@LaunchedEffect

0 commit comments

Comments
 (0)