Apply profile mesh background to loading screens

This commit is contained in:
tapframe 2026-06-25 01:04:17 +05:30
parent 87670e7cb0
commit d767f60d35
3 changed files with 116 additions and 15 deletions

View file

@ -99,6 +99,7 @@ import com.nuvio.app.core.ui.configurePlatformImageLoader
import com.nuvio.app.core.ui.NuvioToastHost
import com.nuvio.app.core.ui.NuvioToastController
import com.nuvio.app.core.ui.NuvioFloatingPrompt
import com.nuvio.app.core.ui.ProfileMeshBackground
import com.nuvio.app.core.ui.TraktListPickerDialog
import com.nuvio.app.core.ui.NuvioTheme
import com.nuvio.app.core.ui.NuvioTokens
@ -166,6 +167,7 @@ import com.nuvio.app.features.profiles.ProfileEditScreen
import com.nuvio.app.features.profiles.ProfileRepository
import com.nuvio.app.features.profiles.ProfileSelectionScreen
import com.nuvio.app.features.profiles.ProfileSwitcherTab
import com.nuvio.app.features.profiles.parseHexColor
import com.nuvio.app.features.profiles.profileAvatarImageUrl
import com.nuvio.app.features.search.SearchScreen
import com.nuvio.app.features.settings.SettingsScreen
@ -733,6 +735,10 @@ private fun MainAppContent(
}
}
val profileState by ProfileRepository.state.collectAsStateWithLifecycle()
val launchOverlayProfileColor = remember(profileState.activeProfile, profileState.profiles) {
val sourceProfile = profileState.activeProfile ?: profileState.profiles.firstOrNull()
sourceProfile?.avatarColorHex?.let(::parseHexColor) ?: Color(0xFF1E88E5)
}
val playerSettingsUiState by remember {
PlayerSettingsRepository.ensureLoaded()
PlayerSettingsRepository.uiState
@ -2913,7 +2919,10 @@ private fun MainAppContent(
enter = fadeIn(),
exit = fadeOut(androidx.compose.animation.core.tween(400)),
) {
AppLaunchOverlay(modifier = Modifier.fillMaxSize())
AppLaunchOverlay(
profileColor = launchOverlayProfileColor,
modifier = Modifier.fillMaxSize(),
)
}
// Auto-dismiss profile switch overlay
@ -3237,15 +3246,19 @@ private fun TabletTopPillItem(
@Composable
private fun AppLaunchOverlay(
profileColor: Color,
modifier: Modifier = Modifier,
) {
val tokens = MaterialTheme.nuvio
Box(
modifier = modifier
.background(tokens.colors.background)
.zIndex(NuvioTokens.Z.dialog),
contentAlignment = Alignment.Center,
) {
ProfileMeshBackground(
profileColor = profileColor,
modifier = Modifier.fillMaxSize(),
)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {

View file

@ -0,0 +1,91 @@
package com.nuvio.app.core.ui
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
@Composable
fun ProfileMeshBackground(
profileColor: Color,
modifier: Modifier = Modifier,
) {
val animatedProfileColor by animateColorAsState(
targetValue = profileColor,
animationSpec = tween(durationMillis = 520),
label = "profileMeshBackgroundColor",
)
val baseColor = Color.Black
val primaryMeshColor = lerp(baseColor, animatedProfileColor, 0.58f)
val secondaryMeshColor = lerp(animatedProfileColor, MaterialTheme.colorScheme.secondary, 0.32f)
val tertiaryMeshColor = lerp(animatedProfileColor, MaterialTheme.colorScheme.tertiary, 0.28f)
Box(
modifier = modifier
.fillMaxSize()
.drawBehind {
val maxDimension = maxOf(size.width, size.height)
drawRect(color = baseColor, size = size)
drawRect(
brush = Brush.radialGradient(
colorStops = arrayOf(
0f to primaryMeshColor.copy(alpha = 0.46f),
0.38f to primaryMeshColor.copy(alpha = 0.2f),
0.72f to primaryMeshColor.copy(alpha = 0.05f),
1f to Color.Transparent,
),
center = Offset(size.width * -0.08f, size.height * -0.02f),
radius = maxDimension * 0.7f,
),
size = size,
)
drawRect(
brush = Brush.radialGradient(
colorStops = arrayOf(
0f to animatedProfileColor.copy(alpha = 0.24f),
0.44f to animatedProfileColor.copy(alpha = 0.09f),
0.78f to animatedProfileColor.copy(alpha = 0.02f),
1f to Color.Transparent,
),
center = Offset(size.width * 0.16f, size.height * 0.18f),
radius = maxDimension * 0.46f,
),
size = size,
)
drawRect(
brush = Brush.radialGradient(
colorStops = arrayOf(
0f to secondaryMeshColor.copy(alpha = 0.16f),
0.5f to secondaryMeshColor.copy(alpha = 0.05f),
1f to Color.Transparent,
),
center = Offset(size.width * 0.9f, size.height * 0.12f),
radius = maxDimension * 0.36f,
),
size = size,
)
drawRect(
brush = Brush.radialGradient(
colorStops = arrayOf(
0f to tertiaryMeshColor.copy(alpha = 0.08f),
0.52f to tertiaryMeshColor.copy(alpha = 0.03f),
1f to Color.Transparent,
),
center = Offset(size.width * 0.28f, size.height * 0.4f),
radius = maxDimension * 0.28f,
),
size = size,
)
},
)
}

View file

@ -46,7 +46,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
@ -59,6 +58,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.core.ui.ProfileMeshBackground
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.*
@ -100,26 +100,23 @@ fun ProfileSelectionScreen(
}
val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
val backgroundProfileColor = remember(profileState.activeProfile, profileState.profiles) {
val sourceProfile = profileState.activeProfile ?: profileState.profiles.firstOrNull()
sourceProfile?.avatarColorHex?.let(::parseHexColor) ?: Color(0xFF1E88E5)
}
BoxWithConstraints(
modifier = modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.background,
MaterialTheme.colorScheme.background,
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.15f),
),
),
)
.padding(top = statusBarTop),
.fillMaxSize(),
) {
val isTabletLayout = maxWidth >= 768.dp
ProfileMeshBackground(profileColor = backgroundProfileColor)
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = statusBarTop)
.then(
if (isTabletLayout) {
Modifier
@ -137,7 +134,7 @@ fun ProfileSelectionScreen(
text = stringResource(Res.string.profile_who_is_watching),
style = MaterialTheme.typography.headlineLarge.copy(
fontSize = 30.sp,
letterSpacing = (-0.5).sp,
letterSpacing = 0.sp,
),
color = MaterialTheme.colorScheme.onBackground,
fontWeight = FontWeight.Bold,