diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt index b298b503..d33a90c3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt @@ -202,10 +202,12 @@ object CollectionEditorRepository { fun addCatalogSource(catalog: AvailableCatalog) { val folder = _uiState.value.editingFolder ?: return + val defaultGenre = if (catalog.genreRequired) catalog.genreOptions.firstOrNull() else null val source = CollectionCatalogSource( addonId = catalog.addonId, type = catalog.type, catalogId = catalog.catalogId, + genre = defaultGenre, ) if (folder.catalogSources.any { it.addonId == source.addonId && it.type == source.type && it.catalogId == source.catalogId @@ -225,6 +227,16 @@ object CollectionEditorRepository { ) } + fun updateCatalogSourceGenre(index: Int, genre: String?) { + val folder = _uiState.value.editingFolder ?: return + if (index !in folder.catalogSources.indices) return + val updated = folder.catalogSources.toMutableList() + updated[index] = updated[index].copy(genre = genre) + _uiState.value = _uiState.value.copy( + editingFolder = folder.copy(catalogSources = updated), + ) + } + fun toggleCatalogSource(catalog: AvailableCatalog) { val folder = _uiState.value.editingFolder ?: return val existingIndex = folder.catalogSources.indexOfFirst { diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt index 88694fff..6aafc8a3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt @@ -697,6 +697,10 @@ private fun FolderEditorSheet( } itemsIndexed(folder.catalogSources) { index, source -> + val matchingCatalog = state.availableCatalogs.find { + it.addonId == source.addonId && it.type == source.type && it.catalogId == source.catalogId + } + val genreOptions = matchingCatalog?.genreOptions.orEmpty() NuvioSurfaceCard { Row( verticalAlignment = Alignment.CenterVertically, @@ -711,7 +715,10 @@ private fun FolderEditorSheet( overflow = TextOverflow.Ellipsis, ) Text( - text = source.addonId, + text = buildString { + append(source.addonId) + if (source.genre != null) append(" · ${source.genre}") + }, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, @@ -730,6 +737,51 @@ private fun FolderEditorSheet( ) } } + if (genreOptions.isNotEmpty()) { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Genre", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + ) + Spacer(modifier = Modifier.height(4.dp)) + FlowRow( + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + FilterChip( + selected = source.genre == null, + onClick = { CollectionEditorRepository.updateCatalogSourceGenre(index, null) }, + label = { Text("All") }, + leadingIcon = if (source.genre == null) { + { + Icon( + imageVector = Icons.Rounded.Check, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + } + } else null, + ) + genreOptions.forEach { genre -> + FilterChip( + selected = source.genre == genre, + onClick = { CollectionEditorRepository.updateCatalogSourceGenre(index, genre) }, + label = { Text(genre) }, + leadingIcon = if (source.genre == genre) { + { + Icon( + imageVector = Icons.Rounded.Check, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + } + } else null, + ) + } + } + } } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt index 1130c8e8..98c60457 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt @@ -27,6 +27,7 @@ data class CollectionCatalogSource( val addonId: String, val type: String, val catalogId: String, + val genre: String? = null, ) @Immutable @@ -73,6 +74,8 @@ data class AvailableCatalog( val type: String, val catalogId: String, val catalogName: String, + val genreOptions: List = emptyList(), + val genreRequired: Boolean = false, ) @Serializable diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt index f0c214ac..b2d19bbe 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt @@ -157,14 +157,17 @@ object CollectionRepository { addon to manifest }.flatMap { (addon, manifest) -> manifest.catalogs - .filter { catalog -> catalog.extra.none { it.isRequired } } + .filter { catalog -> catalog.extra.none { it.isRequired && it.name != "genre" } } .map { catalog -> + val genreExtra = catalog.extra.firstOrNull { it.name == "genre" } AvailableCatalog( addonId = manifest.id, addonName = addon.displayTitle, type = catalog.type, catalogId = catalog.id, catalogName = catalog.name, + genreOptions = genreExtra?.options.orEmpty(), + genreRequired = genreExtra?.isRequired == true, ) } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt index 68c9b844..2328eb75 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt @@ -24,6 +24,7 @@ data class FolderTab( val manifestUrl: String? = null, val type: String = "", val catalogId: String = "", + val genre: String? = null, val supportsPagination: Boolean = false, val items: List = emptyList(), val isLoading: Boolean = true, @@ -123,13 +124,15 @@ object FolderDetailRepository { val typeLabel = source.type.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } + val genreSuffix = if (source.genre != null) " · ${source.genre}" else "" add( FolderTab( - label = "$label ($typeLabel)", + label = "$label ($typeLabel)$genreSuffix", typeLabel = typeLabel, manifestUrl = addon?.manifestUrl, type = source.type, catalogId = source.catalogId, + genre = source.genre, supportsPagination = catalog?.supportsPagination() == true, isLoading = true, ), @@ -236,6 +239,7 @@ object FolderDetailRepository { manifestUrl = manifestUrl, type = currentTab.type, catalogId = currentTab.catalogId, + genre = currentTab.genre, skip = requestedSkip.takeIf { it > 0 }, ) }.onSuccess { page ->