diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt index e83304d..2180ebc 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/overview/OverviewScreen.kt @@ -13,7 +13,6 @@ import androiddev.apps.vbox.client.shared.generated.resources.star import androiddev.apps.vbox.client.shared.generated.resources.star_unchecked import androiddev.apps.vbox.client.shared.generated.resources.user_tags import androiddev.apps.vbox.client.shared.generated.resources.watch -import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring @@ -21,14 +20,21 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -50,11 +56,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -100,21 +108,19 @@ fun OverviewScreen(component: OverviewComponent) { scrollBehavior = scrollBehavior, snackbarState = snackbarState, content = { padding -> - AnimatedContent(details) { - when { + when { - errorState != null -> ErrorAndRetry( - errorMsg = errorState!! - ) { component.loadDetailsIfNeeded() } + errorState != null -> ErrorAndRetry( + errorMsg = errorState!! + ) { component.loadDetailsIfNeeded() } - details != null -> DetailsInfo( - details = details!!, - paddingValues = padding, - onWatchClicked = { component.watch() } - ) + details != null -> DetailsInfo( + details = details!!, + paddingValues = padding, + onWatchClicked = { component.watch() } + ) - else -> Loading() - } + else -> Loading() } if (tagsBottomSheetVisible) { @@ -261,7 +267,7 @@ fun DetailsInfo( modifier = Modifier.applyBlurSource(blur), contentPadding = paddingValues ) { - item { OverviewTitle(details) } + item { Poster(details.poster) } item { OverviewDescription(details, showDetails) } item { Button( @@ -274,8 +280,8 @@ fun DetailsInfo( Text(stringResource(Res.string.watch)) } - // Чистый фокус без сдвига кадров через withFrameNanos LaunchedEffect(Unit) { + withFrameNanos {} requester.requestFocus() } } @@ -291,7 +297,6 @@ private fun OverviewDescription( modifier = Modifier .fillMaxWidth() .padding(4.dp) - // Самая важная магия: плавно анимирует высоту колонки при изменении maxLines текста .animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMediumLow)) ) { Text( @@ -307,7 +312,6 @@ private fun OverviewDescription( overflow = TextOverflow.Ellipsis ) - // Вместо резкого 'if' плавно убираем кнопку разворачивания AnimatedVisibility( visible = !showDetails.value, enter = fadeIn(), @@ -319,7 +323,7 @@ private fun OverviewDescription( style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.primary, modifier = Modifier - .padding(top = 8.dp) // Небольшой отступ сверху от основного текста + .padding(top = 8.dp) .clickable { showDetails.value = true } ) } @@ -327,16 +331,49 @@ private fun OverviewDescription( } @Composable -private fun OverviewTitle(details: VideoDetails) { - Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) { - Image( - painter = rememberImagePainter(details.poster), - contentDescription = null, - modifier = Modifier - .padding(8.dp) // Добавим легкий отступ, чтобы картинка не липла к краям экрана - .clip(RoundedCornerShape(12.dp)), // На современных девайсах 12.dp выглядит аккуратнее, чем 8% - contentScale = ContentScale.Crop, - ) +private fun Poster(posterUrl: String, modifier: Modifier = Modifier) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxWidth() + ) { + Card( + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerLow + ), + elevation = CardDefaults.cardElevation( + defaultElevation = 4.dp + ), + modifier = modifier.height(200.dp) + .aspectRatio(0.7f) + ) { + Box(modifier = Modifier.fillMaxSize()) { + Image( + painter = rememberImagePainter(posterUrl), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + + Box( + modifier = Modifier.fillMaxSize() + .background( + Brush.verticalGradient( + 0.6f to Color.Transparent, + 1.0f to Color.Black.copy(alpha = 0.6f) + ) + ) + ) + + Box( + modifier = Modifier.fillMaxSize() + .border( + width = 1.dp, + color = Color.White.copy(alpha = 0.15f), + shape = RoundedCornerShape(12.dp) + ) + ) + } + } } } -