mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-22 17:52:06 +00:00
ref: adjust cloud library ui and sorting
This commit is contained in:
parent
0bfd2cb99c
commit
800d7160b1
4 changed files with 253 additions and 219 deletions
|
|
@ -1350,6 +1350,8 @@
|
|||
<string name="cloud_library_playable_file_count">%1$d playable files</string>
|
||||
<string name="cloud_library_provider_all">All</string>
|
||||
<string name="cloud_library_refresh">Refresh cloud library</string>
|
||||
<string name="cloud_library_select_provider">Select provider</string>
|
||||
<string name="cloud_library_select_type">Select type</string>
|
||||
<string name="cloud_library_status_ready">Ready to play</string>
|
||||
<string name="cloud_library_type_all">All</string>
|
||||
<string name="cloud_library_type_torrents">Torrents</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class NuvioDropdownOption(
|
||||
val key: String,
|
||||
val label: String,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NuvioDropdownChip(
|
||||
title: String,
|
||||
label: String,
|
||||
selectedKey: String?,
|
||||
options: List<NuvioDropdownOption>,
|
||||
enabled: Boolean = true,
|
||||
onSelected: (NuvioDropdownOption) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var isSheetVisible by remember { mutableStateOf(false) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.then(
|
||||
if (enabled) {
|
||||
Modifier.clickable { isSheetVisible = true }
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = if (enabled) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
}
|
||||
|
||||
if (isSheetVisible) {
|
||||
NuvioDropdownOptionsSheet(
|
||||
title = title,
|
||||
options = options,
|
||||
selectedKey = selectedKey,
|
||||
sheetState = sheetState,
|
||||
onDismiss = {
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
onSelected = { option ->
|
||||
onSelected(option)
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun NuvioDropdownOptionsSheet(
|
||||
title: String,
|
||||
options: List<NuvioDropdownOption>,
|
||||
selectedKey: String?,
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
onSelected: (NuvioDropdownOption) -> Unit,
|
||||
) {
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = nuvioSafeBottomPadding(16.dp)),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
NuvioBottomSheetDivider()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 420.dp),
|
||||
) {
|
||||
itemsIndexed(options) { index, option ->
|
||||
NuvioBottomSheetActionRow(
|
||||
title = option.label,
|
||||
onClick = { onSelected(option) },
|
||||
trailingContent = {
|
||||
if (option.key == selectedKey) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Check,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (index < options.lastIndex) {
|
||||
NuvioBottomSheetDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import androidx.compose.animation.core.tween
|
|||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -19,10 +20,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||
|
|
@ -57,6 +58,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.nuvio.app.core.i18n.localizedByteUnit
|
||||
import com.nuvio.app.core.network.NetworkCondition
|
||||
import com.nuvio.app.core.network.NetworkStatusRepository
|
||||
import com.nuvio.app.core.ui.NuvioDropdownChip
|
||||
import com.nuvio.app.core.ui.NuvioDropdownOption
|
||||
import com.nuvio.app.core.ui.NuvioScreen
|
||||
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
||||
import com.nuvio.app.core.ui.NuvioScreenHeader
|
||||
|
|
@ -204,6 +207,7 @@ fun LibraryScreen(
|
|||
selectedCloudItemKey = selectedCloudItemKey,
|
||||
onProviderSelected = {
|
||||
selectedProviderId = it
|
||||
selectedTypeName = null
|
||||
selectedCloudItemKey = null
|
||||
},
|
||||
onTypeSelected = {
|
||||
|
|
@ -337,9 +341,15 @@ private fun LazyListScope.cloudLibraryContent(
|
|||
}
|
||||
|
||||
else -> {
|
||||
val filteredItems = uiState.items
|
||||
val providerItems = uiState.items
|
||||
.filter { item -> selectedProviderId == null || item.providerId == selectedProviderId }
|
||||
.filter { item -> selectedType == null || item.type == selectedType }
|
||||
val availableTypes = providerItems
|
||||
.map { item -> item.type }
|
||||
.distinct()
|
||||
.sortedBy { type -> type.ordinal }
|
||||
val effectiveSelectedType = selectedType?.takeIf { type -> type in availableTypes }
|
||||
val filteredItems = providerItems
|
||||
.filter { item -> effectiveSelectedType == null || item.type == effectiveSelectedType }
|
||||
val selectedItem = filteredItems.firstOrNull { it.stableKey == selectedCloudItemKey }
|
||||
|
||||
if (selectedItem != null) {
|
||||
|
|
@ -355,7 +365,8 @@ private fun LazyListScope.cloudLibraryContent(
|
|||
CloudLibraryToolbar(
|
||||
uiState = uiState,
|
||||
selectedProviderId = selectedProviderId,
|
||||
selectedType = selectedType,
|
||||
selectedType = effectiveSelectedType,
|
||||
availableTypes = availableTypes,
|
||||
onProviderSelected = onProviderSelected,
|
||||
onTypeSelected = onTypeSelected,
|
||||
onRefresh = onRefresh,
|
||||
|
|
@ -445,11 +456,41 @@ private fun CloudLibraryToolbar(
|
|||
uiState: CloudLibraryUiState,
|
||||
selectedProviderId: String?,
|
||||
selectedType: CloudLibraryItemType?,
|
||||
availableTypes: List<CloudLibraryItemType>,
|
||||
onProviderSelected: (String?) -> Unit,
|
||||
onTypeSelected: (CloudLibraryItemType?) -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val providerOptions = buildList {
|
||||
add(NuvioDropdownOption(key = "", label = stringResource(Res.string.cloud_library_provider_all)))
|
||||
addAll(
|
||||
uiState.providers.map { provider ->
|
||||
NuvioDropdownOption(
|
||||
key = provider.providerId,
|
||||
label = provider.providerName,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
val typeOptions = buildList {
|
||||
add(NuvioDropdownOption(key = "", label = stringResource(Res.string.cloud_library_type_all)))
|
||||
addAll(
|
||||
availableTypes.map { type ->
|
||||
NuvioDropdownOption(
|
||||
key = type.name,
|
||||
label = cloudLibraryTypeLabel(type),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
val selectedProviderName = uiState.providers
|
||||
.firstOrNull { provider -> provider.providerId == selectedProviderId }
|
||||
?.providerName
|
||||
?: stringResource(Res.string.cloud_library_provider_all)
|
||||
val selectedTypeLabel = selectedType?.let { type -> cloudLibraryTypeLabel(type) }
|
||||
?: stringResource(Res.string.cloud_library_type_all)
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
|
@ -460,30 +501,35 @@ private fun CloudLibraryToolbar(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = Modifier.weight(1f),
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(end = 8.dp),
|
||||
) {
|
||||
item {
|
||||
LibraryChip(
|
||||
label = stringResource(Res.string.cloud_library_provider_all),
|
||||
selected = selectedProviderId == null,
|
||||
onClick = { onProviderSelected(null) },
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = uiState.providers,
|
||||
key = { provider -> provider.providerId },
|
||||
) { provider ->
|
||||
LibraryChip(
|
||||
label = provider.providerName,
|
||||
selected = selectedProviderId == provider.providerId,
|
||||
loading = provider.isLoading,
|
||||
error = !provider.errorMessage.isNullOrBlank(),
|
||||
onClick = { onProviderSelected(provider.providerId) },
|
||||
)
|
||||
}
|
||||
NuvioDropdownChip(
|
||||
title = stringResource(Res.string.cloud_library_select_provider),
|
||||
label = selectedProviderName,
|
||||
selectedKey = selectedProviderId.orEmpty(),
|
||||
options = providerOptions,
|
||||
enabled = providerOptions.size > 1,
|
||||
onSelected = { option ->
|
||||
onProviderSelected(option.key.ifBlank { null })
|
||||
},
|
||||
)
|
||||
NuvioDropdownChip(
|
||||
title = stringResource(Res.string.cloud_library_select_type),
|
||||
label = selectedTypeLabel,
|
||||
selectedKey = selectedType?.name.orEmpty(),
|
||||
options = typeOptions,
|
||||
enabled = typeOptions.size > 1,
|
||||
onSelected = { option ->
|
||||
val type = option.key
|
||||
.takeIf { it.isNotBlank() }
|
||||
?.let(CloudLibraryItemType::valueOf)
|
||||
onTypeSelected(type)
|
||||
},
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onRefresh) {
|
||||
Icon(
|
||||
|
|
@ -493,28 +539,6 @@ private fun CloudLibraryToolbar(
|
|||
)
|
||||
}
|
||||
}
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(end = 16.dp),
|
||||
) {
|
||||
item {
|
||||
LibraryChip(
|
||||
label = stringResource(Res.string.cloud_library_type_all),
|
||||
selected = selectedType == null,
|
||||
onClick = { onTypeSelected(null) },
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = CloudLibraryItemType.entries,
|
||||
key = { type -> type.name },
|
||||
) { type ->
|
||||
LibraryChip(
|
||||
label = cloudLibraryTypeLabel(type),
|
||||
selected = selectedType == type,
|
||||
onClick = { onTypeSelected(type) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -841,24 +865,16 @@ private fun CloudLibrarySkeletonToolbar(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
CloudSkeletonBlock(brush = brush, width = 52.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 86.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 92.dp, height = 34.dp, cornerRadius = 12.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 78.dp, height = 34.dp, cornerRadius = 12.dp)
|
||||
CloudSkeletonBlock(
|
||||
brush = brush,
|
||||
modifier = Modifier.weight(1f),
|
||||
height = 34.dp,
|
||||
cornerRadius = 18.dp,
|
||||
cornerRadius = 12.dp,
|
||||
)
|
||||
CloudSkeletonBlock(brush = brush, width = 40.dp, height = 40.dp, cornerRadius = 20.dp)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
CloudSkeletonBlock(brush = brush, width = 52.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 82.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 72.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
CloudSkeletonBlock(brush = brush, width = 60.dp, height = 34.dp, cornerRadius = 18.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.nuvio.app.features.search
|
|||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -13,31 +12,16 @@ 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.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -49,12 +33,9 @@ import androidx.compose.ui.unit.sp
|
|||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.network.NetworkCondition
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.ui.NuvioDropdownChip
|
||||
import com.nuvio.app.core.ui.NuvioDropdownOption
|
||||
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetActionRow
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.core.ui.nuvioSafeBottomPadding
|
||||
import com.nuvio.app.core.ui.NuvioPosterWatchedOverlay
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
|
|
@ -62,7 +43,6 @@ import com.nuvio.app.features.home.MetaPreview
|
|||
import com.nuvio.app.features.home.PosterShape
|
||||
import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import kotlinx.coroutines.launch
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
|
|
@ -174,19 +154,19 @@ private fun DiscoverFilterRow(
|
|||
modifier = modifier.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
DiscoverDropdownChip(
|
||||
NuvioDropdownChip(
|
||||
title = stringResource(Res.string.discover_select_type),
|
||||
label = state.selectedType?.displayTypeLabel() ?: stringResource(Res.string.discover_type),
|
||||
selectedKey = state.selectedType,
|
||||
options = state.typeOptions.map { DiscoverOptionItem(key = it, label = it.displayTypeLabel()) },
|
||||
options = state.typeOptions.map { NuvioDropdownOption(key = it, label = it.displayTypeLabel()) },
|
||||
enabled = state.typeOptions.isNotEmpty(),
|
||||
onSelected = { onTypeSelected(it.key) },
|
||||
)
|
||||
DiscoverDropdownChip(
|
||||
NuvioDropdownChip(
|
||||
title = stringResource(Res.string.discover_select_catalog),
|
||||
label = state.selectedCatalog?.catalogName ?: stringResource(Res.string.discover_catalog),
|
||||
selectedKey = state.selectedCatalogKey,
|
||||
options = state.catalogOptions.map { option -> DiscoverOptionItem(key = option.key, label = option.catalogName) },
|
||||
options = state.catalogOptions.map { option -> NuvioDropdownOption(key = option.key, label = option.catalogName) },
|
||||
enabled = state.catalogOptions.isNotEmpty(),
|
||||
onSelected = { onCatalogSelected(it.key) },
|
||||
)
|
||||
|
|
@ -194,11 +174,11 @@ private fun DiscoverFilterRow(
|
|||
val selectedCatalog = state.selectedCatalog
|
||||
val genreOptions = buildList {
|
||||
if (selectedCatalog?.genreRequired != true) {
|
||||
add(DiscoverOptionItem(key = "", label = stringResource(Res.string.discover_all_genres)))
|
||||
add(NuvioDropdownOption(key = "", label = stringResource(Res.string.discover_all_genres)))
|
||||
}
|
||||
addAll(state.genreOptions.map { genre -> DiscoverOptionItem(key = genre, label = genre) })
|
||||
addAll(state.genreOptions.map { genre -> NuvioDropdownOption(key = genre, label = genre) })
|
||||
}
|
||||
DiscoverDropdownChip(
|
||||
NuvioDropdownChip(
|
||||
title = stringResource(Res.string.discover_select_genre),
|
||||
label = state.selectedGenre ?: stringResource(Res.string.discover_all_genres),
|
||||
selectedKey = state.selectedGenre ?: "",
|
||||
|
|
@ -211,132 +191,6 @@ private fun DiscoverFilterRow(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun DiscoverDropdownChip(
|
||||
title: String,
|
||||
label: String,
|
||||
selectedKey: String?,
|
||||
options: List<DiscoverOptionItem>,
|
||||
enabled: Boolean,
|
||||
onSelected: (DiscoverOptionItem) -> Unit,
|
||||
) {
|
||||
var isSheetVisible by remember { mutableStateOf(false) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.then(
|
||||
if (enabled) {
|
||||
Modifier.clickable { isSheetVisible = true }
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = if (enabled) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
}
|
||||
|
||||
if (isSheetVisible) {
|
||||
DiscoverOptionsSheet(
|
||||
title = title,
|
||||
options = options,
|
||||
selectedKey = selectedKey,
|
||||
sheetState = sheetState,
|
||||
onDismiss = {
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
onSelected = { option ->
|
||||
onSelected(option)
|
||||
coroutineScope.launch {
|
||||
dismissNuvioBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismiss = { isSheetVisible = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun DiscoverOptionsSheet(
|
||||
title: String,
|
||||
options: List<DiscoverOptionItem>,
|
||||
selectedKey: String?,
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
onSelected: (DiscoverOptionItem) -> Unit,
|
||||
) {
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = nuvioSafeBottomPadding(16.dp)),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
NuvioBottomSheetDivider()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 420.dp),
|
||||
) {
|
||||
itemsIndexed(options) { index, option ->
|
||||
NuvioBottomSheetActionRow(
|
||||
title = option.label,
|
||||
onClick = { onSelected(option) },
|
||||
trailingContent = {
|
||||
if (option.key == selectedKey) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Check,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (index < options.lastIndex) {
|
||||
NuvioBottomSheetDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DiscoverGridRow(
|
||||
items: List<MetaPreview>,
|
||||
|
|
@ -518,11 +372,6 @@ private fun DiscoverEmptyStateCard(
|
|||
)
|
||||
}
|
||||
|
||||
private data class DiscoverOptionItem(
|
||||
val key: String,
|
||||
val label: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun String.displayTypeLabel(): String =
|
||||
when (lowercase()) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue