diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt index 0da4826..f5932ef 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt @@ -1,6 +1,9 @@ package com.fluxa.app.shared +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -356,6 +359,13 @@ fun FluxaAppHost( } } + BoxWithConstraints(modifier = Modifier.fillMaxSize()) { + val widthClass = if (deviceType == com.fluxa.app.ui.catalog.DeviceType.TV) { + com.fluxa.app.ui.catalog.WindowWidthClass.Expanded + } else { + com.fluxa.app.ui.catalog.widthClassFor(maxWidth) + } + CompositionLocalProvider(com.fluxa.app.ui.catalog.LocalWindowWidthClass provides widthClass) { FluxaApp( state = appState.uiState, deviceType = deviceType, @@ -557,4 +567,6 @@ fun FluxaAppHost( showNavigationBar = showNavigationBar, modifier = modifier ) + } + } } diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/TvSidebarNav.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/TvSidebarNav.kt index 112e03b..d1c7b1c 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/TvSidebarNav.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/TvSidebarNav.kt @@ -1,7 +1,10 @@ +@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) + package com.fluxa.app.shared import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusGroup import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -26,6 +29,7 @@ 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.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector @@ -59,7 +63,9 @@ fun TvSidebarNav( .fillMaxHeight() .width(84.dp) .background(Color.Black.copy(alpha = 0.55f)) - .padding(vertical = 24.dp), + .padding(vertical = 24.dp) + .focusGroup() + .focusRestorer(), verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically) ) { TvSidebarItems.forEach { item -> diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/catalog/TvCatalogHomeScreen.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/catalog/TvCatalogHomeScreen.kt index 8d17aa4..d3524c9 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/catalog/TvCatalogHomeScreen.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/catalog/TvCatalogHomeScreen.kt @@ -1,3 +1,5 @@ +@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) + package com.fluxa.app.shared.feature.catalog import androidx.compose.foundation.background @@ -18,10 +20,16 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowForward +import androidx.compose.foundation.focusGroup import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember 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.focus.focusRestorer import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -38,17 +46,25 @@ fun TvCatalogHomeScreen( language: String? = null, modifier: Modifier = Modifier ) { + val columnFocus = remember { FocusRequester() } Box(modifier = modifier.fillMaxSize()) { if (state.rows.isEmpty()) { TvCatalogHomeLoading(Modifier.fillMaxSize()) } else { + LaunchedEffect(Unit) { runCatching { columnFocus.requestFocus() } } LazyColumn( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .focusRequester(columnFocus) + .focusRestorer(), contentPadding = PaddingValues(top = 44.dp, bottom = 64.dp), verticalArrangement = Arrangement.spacedBy(30.dp) ) { items(state.rows, key = { it.id }) { row -> - Column(verticalArrangement = Arrangement.spacedBy(14.dp)) { + Column( + modifier = Modifier.focusGroup(), + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { Text( text = row.title, color = Color.White, @@ -59,6 +75,7 @@ fun TvCatalogHomeScreen( modifier = Modifier.padding(horizontal = 58.dp) ) LazyRow( + modifier = Modifier.focusRestorer(), contentPadding = PaddingValues(horizontal = 58.dp), horizontalArrangement = Arrangement.spacedBy(20.dp) ) { diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/discover/DiscoverScreen.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/discover/DiscoverScreen.kt index 16472d1..c10013f 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/discover/DiscoverScreen.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/discover/DiscoverScreen.kt @@ -1,8 +1,11 @@ +@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) + package com.fluxa.app.shared.feature.discover import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll +import androidx.compose.ui.focus.focusRestorer import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -59,6 +62,8 @@ import com.fluxa.app.shared.feature.search.SearchResultRows import com.fluxa.app.shared.feature.search.SearchResults import com.fluxa.app.ui.catalog.CatalogCard import com.fluxa.app.ui.catalog.FluxaColors +import com.fluxa.app.ui.catalog.LocalWindowWidthClass +import com.fluxa.app.ui.catalog.gridColumns @Composable fun DiscoverScreen( @@ -141,8 +146,8 @@ fun DiscoverScreen( } LazyVerticalGrid( state = gridState, - columns = GridCells.Fixed(3), - modifier = Modifier.weight(1f).fillMaxWidth(), + columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()), + modifier = Modifier.weight(1f).fillMaxWidth().focusRestorer(), contentPadding = PaddingValues(bottom = 120.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(16.dp) @@ -201,7 +206,7 @@ private fun DiscoverSearchField( @Composable private fun DiscoverSkeletonGrid(modifier: Modifier = Modifier) { LazyVerticalGrid( - columns = GridCells.Fixed(3), + columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()), modifier = modifier.fillMaxWidth(), contentPadding = PaddingValues(bottom = 120.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/library/LibraryScreen.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/library/LibraryScreen.kt index 59a1ede..85c7af0 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/library/LibraryScreen.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/library/LibraryScreen.kt @@ -1,3 +1,5 @@ +@file:OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) + package com.fluxa.app.shared.feature.library import androidx.compose.foundation.background @@ -43,6 +45,7 @@ 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.focusRestorer import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight @@ -55,6 +58,8 @@ import com.fluxa.app.shared.feature.discover.DiscoverFilterOptionUiModel import com.fluxa.app.shared.image.FluxaRemoteImage import com.fluxa.app.ui.catalog.CatalogCard import com.fluxa.app.ui.catalog.FluxaColors +import com.fluxa.app.ui.catalog.LocalWindowWidthClass +import com.fluxa.app.ui.catalog.gridColumns @Composable fun LibraryScreen( @@ -265,11 +270,11 @@ private fun LibraryItemGrid( return } LazyVerticalGrid( - columns = GridCells.Fixed(3), + columns = GridCells.Fixed(LocalWindowWidthClass.current.gridColumns()), contentPadding = PaddingValues(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 120.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize().focusRestorer() ) { items(items, key = { "${it.type}:${it.id}" }) { item -> CatalogCard(model = item.card.copy(showTitleBar = true), onClick = { onItemSelected(item) }) diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/CatalogCard.kt b/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/CatalogCard.kt index e821096..b9a3e5b 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/CatalogCard.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/CatalogCard.kt @@ -72,8 +72,8 @@ fun CatalogCard( .then( if (focused) { Modifier - .scale(1.05f) - .border(2.dp, Color.White, RoundedCornerShape(8.dp)) + .scale(1.1f) + .border(3.dp, Color.White, RoundedCornerShape(8.dp)) } else { Modifier } diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/WindowWidthClass.kt b/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/WindowWidthClass.kt new file mode 100644 index 0000000..419263b --- /dev/null +++ b/shared/src/commonMain/kotlin/com/fluxa/app/ui/catalog/WindowWidthClass.kt @@ -0,0 +1,21 @@ +package com.fluxa.app.ui.catalog + +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +enum class WindowWidthClass { Compact, Medium, Expanded } + +val LocalWindowWidthClass = compositionLocalOf { WindowWidthClass.Compact } + +fun widthClassFor(maxWidth: Dp): WindowWidthClass = when { + maxWidth < 600.dp -> WindowWidthClass.Compact + maxWidth < 840.dp -> WindowWidthClass.Medium + else -> WindowWidthClass.Expanded +} + +fun WindowWidthClass.gridColumns(): Int = when (this) { + WindowWidthClass.Compact -> 3 + WindowWidthClass.Medium -> 5 + WindowWidthClass.Expanded -> 7 +}