mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-08-18 21:18:32 +00:00
Add shared discover screen
This commit is contained in:
parent
fcb9df3de3
commit
4f4b049f2d
3 changed files with 169 additions and 0 deletions
|
|
@ -27,6 +27,9 @@ import com.fluxa.app.shared.feature.catalog.CatalogItemUiModel
|
|||
import com.fluxa.app.shared.feature.detail.DetailAction
|
||||
import com.fluxa.app.shared.feature.detail.DetailScreen
|
||||
import com.fluxa.app.shared.feature.detail.DetailUiState
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverAction
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverScreen
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverUiState
|
||||
import com.fluxa.app.shared.feature.search.SearchAction
|
||||
import com.fluxa.app.shared.feature.search.SearchScreen
|
||||
import com.fluxa.app.shared.feature.search.SearchUiState
|
||||
|
|
@ -58,6 +61,8 @@ fun FluxaApp(
|
|||
onDetailAction: (DetailAction) -> Unit = {},
|
||||
searchState: SearchUiState? = null,
|
||||
onSearchAction: (SearchAction) -> Unit = {},
|
||||
discoverState: DiscoverUiState? = null,
|
||||
onDiscoverAction: (DiscoverAction) -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
MaterialTheme {
|
||||
|
|
@ -85,6 +90,13 @@ fun FluxaApp(
|
|||
onItemSelected = { item -> onSearchAction(SearchAction.ItemSelected(item)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
state.destination == FluxaDestination.Discover && discoverState != null -> DiscoverScreen(
|
||||
state = discoverState,
|
||||
language = state.language,
|
||||
onFiltersChanged = { filters -> onDiscoverAction(DiscoverAction.FiltersChanged(filters)) },
|
||||
onItemSelected = { item -> onDiscoverAction(DiscoverAction.ItemSelected(item)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
state.destination == FluxaDestination.Home -> FluxaHomeContent(
|
||||
state = state,
|
||||
onCatalogAction = onCatalogAction,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@ import com.fluxa.app.shared.feature.catalog.CatalogHomeStore
|
|||
import com.fluxa.app.shared.feature.detail.DetailAction
|
||||
import com.fluxa.app.shared.feature.detail.DetailDataSource
|
||||
import com.fluxa.app.shared.feature.detail.DetailStore
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverAction
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverDataSource
|
||||
import com.fluxa.app.shared.feature.discover.DiscoverStore
|
||||
import com.fluxa.app.shared.feature.search.SearchAction
|
||||
import com.fluxa.app.shared.feature.search.SearchDataSource
|
||||
import com.fluxa.app.shared.feature.search.SearchStore
|
||||
import com.fluxa.app.shared.platform.FluxaDetailServices
|
||||
import com.fluxa.app.shared.platform.FluxaDiscoverServices
|
||||
import com.fluxa.app.shared.platform.FluxaPlatformServices
|
||||
import com.fluxa.app.shared.platform.FluxaSearchServices
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -31,6 +35,7 @@ fun FluxaAppHost(
|
|||
FluxaAppHost(
|
||||
catalogHomeDataSource = platformServices.catalogHomeDataSource,
|
||||
detailDataSource = (platformServices as? FluxaDetailServices)?.detailDataSource,
|
||||
discoverDataSource = (platformServices as? FluxaDiscoverServices)?.discoverDataSource,
|
||||
searchDataSource = (platformServices as? FluxaSearchServices)?.searchDataSource,
|
||||
language = language,
|
||||
onCatalogAction = onCatalogAction,
|
||||
|
|
@ -42,6 +47,7 @@ fun FluxaAppHost(
|
|||
fun FluxaAppHost(
|
||||
catalogHomeDataSource: CatalogHomeDataSource,
|
||||
detailDataSource: DetailDataSource? = null,
|
||||
discoverDataSource: DiscoverDataSource? = null,
|
||||
searchDataSource: SearchDataSource? = null,
|
||||
language: String? = null,
|
||||
onCatalogAction: (CatalogAction) -> Unit = {},
|
||||
|
|
@ -56,6 +62,10 @@ fun FluxaAppHost(
|
|||
remember(source) { SearchStore(source, scope) }
|
||||
}
|
||||
val searchState = searchStore?.state?.collectAsState()?.value
|
||||
val discoverStore = discoverDataSource?.let { source ->
|
||||
remember(source) { DiscoverStore(source, scope) }
|
||||
}
|
||||
val discoverState = discoverStore?.state?.collectAsState()?.value
|
||||
val appState = rememberFluxaAppState()
|
||||
val selectedDetail = appState.uiState.selectedDetail
|
||||
val detailStore = selectedDetail?.let { item ->
|
||||
|
|
@ -112,6 +122,16 @@ fun FluxaAppHost(
|
|||
searchStore?.dispatch(action)
|
||||
}
|
||||
},
|
||||
discoverState = discoverState,
|
||||
onDiscoverAction = { action ->
|
||||
if (action is DiscoverAction.ItemSelected) {
|
||||
appState.selectDetail(action.item)
|
||||
onCatalogAction(CatalogAction.ItemSelected(action.item))
|
||||
}
|
||||
scope.launch {
|
||||
discoverStore?.dispatch(action)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
package com.fluxa.app.shared.feature.discover
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.fluxa.app.common.AppStrings
|
||||
import com.fluxa.app.shared.feature.catalog.CatalogItemUiModel
|
||||
import com.fluxa.app.ui.catalog.CatalogCard
|
||||
import com.fluxa.app.ui.catalog.FluxaColors
|
||||
|
||||
@Composable
|
||||
fun DiscoverScreen(
|
||||
state: DiscoverUiState,
|
||||
language: String?,
|
||||
onFiltersChanged: (DiscoverFiltersUiModel) -> Unit,
|
||||
onItemSelected: (CatalogItemUiModel) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(FluxaColors.background)
|
||||
.padding(horizontal = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = AppStrings.t(language, "nav.discover"),
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
DiscoverFilters(
|
||||
filters = state.filters,
|
||||
catalogOptions = state.catalogOptions,
|
||||
genreOptions = state.genreOptions,
|
||||
language = language,
|
||||
onFiltersChanged = onFiltersChanged
|
||||
)
|
||||
when {
|
||||
state.isLoading && state.results.isEmpty() -> Box(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) { CircularProgressIndicator(color = Color.White) }
|
||||
state.results.isEmpty() -> Box(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) { Text(AppStrings.t(language, "auto.no_results_yet"), color = Color.White) }
|
||||
else -> LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 128.dp),
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
contentPadding = PaddingValues(bottom = 20.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
items(state.results, key = { "${it.type}:${it.id}" }) { item ->
|
||||
CatalogCard(model = item.card, onClick = { onItemSelected(item) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DiscoverFilters(
|
||||
filters: DiscoverFiltersUiModel,
|
||||
catalogOptions: List<DiscoverFilterOptionUiModel>,
|
||||
genreOptions: List<DiscoverFilterOptionUiModel>,
|
||||
language: String?,
|
||||
onFiltersChanged: (DiscoverFiltersUiModel) -> Unit
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
DiscoverFilterRow(
|
||||
options = listOf(
|
||||
DiscoverFilterOptionUiModel("movie", AppStrings.t(language, "auto.movie")),
|
||||
DiscoverFilterOptionUiModel("series", AppStrings.t(language, "auto.series"))
|
||||
),
|
||||
selectedId = filters.contentType,
|
||||
onSelected = { value ->
|
||||
onFiltersChanged(DiscoverFiltersUiModel(contentType = value.orEmpty()))
|
||||
}
|
||||
)
|
||||
if (catalogOptions.isNotEmpty()) {
|
||||
DiscoverFilterRow(
|
||||
options = catalogOptions,
|
||||
selectedId = filters.catalogKey,
|
||||
onSelected = { value ->
|
||||
onFiltersChanged(filters.copy(catalogKey = value, genre = null))
|
||||
}
|
||||
)
|
||||
}
|
||||
if (genreOptions.isNotEmpty()) {
|
||||
DiscoverFilterRow(
|
||||
options = genreOptions,
|
||||
selectedId = filters.genre,
|
||||
onSelected = { value ->
|
||||
onFiltersChanged(filters.copy(genre = value))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DiscoverFilterRow(
|
||||
options: List<DiscoverFilterOptionUiModel>,
|
||||
selectedId: String?,
|
||||
onSelected: (String?) -> Unit
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
options.forEach { option ->
|
||||
val selected = option.id == selectedId
|
||||
Text(
|
||||
text = option.label,
|
||||
color = if (selected) Color.White else Color.White.copy(alpha = 0.6f),
|
||||
fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium,
|
||||
modifier = Modifier.clickable { onSelected(option.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue