Keep the shared shell mounted across all mobile navigation, add Discover skeleton

The shared shell was only mounted while on Home/Search/Discover/
Calendar; navigating to any other native screen (Detail, Sources,
Player, Settings, AddonStore) disposed it entirely, so returning to
any of those four tabs remounted from scratch — fresh stores, fresh
StateFlow defaults (isLoading=true, empty items), and a real re-fetch,
even though the underlying HomeViewModel data was already cached. That
produced the "blank calendar/grid when coming back from another page"
symptom.

FluxaAppHost (mobile) is now always composed as a background layer
behind AppRoutesHost's native AnimatedContent, which only draws on top
(opaque) when the active screen isn't one of those four. The shell
itself is never disposed by navigating elsewhere, so its stores keep
their last-known state and returning to a tab is instant.

Also replaced Discover's full-screen blocking spinner on first load
with a skeleton grid (18 placeholder cards) so the filter row stays
visible and the transition to real results doesn't hide the whole
screen behind a spinner.
This commit is contained in:
KhooLy 2026-07-13 21:29:15 +03:00
parent 9566988f11
commit 79fe70cfc2
2 changed files with 27 additions and 5 deletions

View file

@ -15,6 +15,7 @@ import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
@ -125,7 +126,7 @@ internal fun AppRoutesHost(
null
}
if (mobileSharedDestination != null) {
if (deviceType == DeviceType.Mobile) {
com.fluxa.app.shared.FluxaAppHost(
platformServices = androidFluxaPlatformServices!!,
language = activeProfile?.language,
@ -136,6 +137,9 @@ internal fun AppRoutesHost(
.fillMaxSize()
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
)
}
if (mobileSharedDestination != null) {
return
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -95,10 +96,7 @@ fun DiscoverScreen(
onFiltersChanged = onFiltersChanged
)
when {
state.isLoading && state.results.isEmpty() -> Box(
modifier = Modifier.weight(1f).fillMaxWidth(),
contentAlignment = Alignment.Center
) { CircularProgressIndicator(color = Color.White) }
state.isLoading && state.results.isEmpty() -> DiscoverSkeletonGrid(modifier = Modifier.weight(1f))
state.results.isEmpty() -> Box(
modifier = Modifier.weight(1f).fillMaxWidth(),
contentAlignment = Alignment.Center
@ -119,6 +117,26 @@ fun DiscoverScreen(
}
}
@Composable
private fun DiscoverSkeletonGrid(modifier: Modifier = Modifier) {
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = modifier.fillMaxWidth(),
contentPadding = PaddingValues(bottom = 20.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(18, key = { it }) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(2f / 3f)
.background(FluxaColors.surfaceCard, RoundedCornerShape(10.dp))
)
}
}
}
@Composable
private fun DiscoverFilters(
filters: DiscoverFiltersUiModel,