mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-08-05 19:59:51 +00:00
feat: add genre selection and tabs to HomeScreen for improved content navigation
This commit is contained in:
parent
7d4d11705a
commit
8f9c2540a2
@ -49,8 +49,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.em
|
import androidx.compose.ui.unit.em
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseGenre
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
||||||
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
||||||
import dev.krtirtho.spotube.PlatformType
|
import dev.krtirtho.spotube.PlatformType
|
||||||
|
import dev.krtirtho.spotube.core.ui.base.ChipTab
|
||||||
import dev.krtirtho.spotube.core.ui.component.AlbumCard
|
import dev.krtirtho.spotube.core.ui.component.AlbumCard
|
||||||
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
|
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
|
||||||
import dev.krtirtho.spotube.core.ui.component.ArtistCard
|
import dev.krtirtho.spotube.core.ui.component.ArtistCard
|
||||||
@ -80,6 +83,15 @@ fun HomeScreen(viewModel: HomeScreenViewModel) {
|
|||||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
val selectedGenreId = when (val s = state) {
|
||||||
|
is HomeScreenState.Data -> s.selectedGenreId
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(selectedGenreId) {
|
||||||
|
listState.scrollToItem(0)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
snapshotFlow { listState.layoutInfo }
|
snapshotFlow { listState.layoutInfo }
|
||||||
.map { layoutInfo ->
|
.map { layoutInfo ->
|
||||||
@ -119,6 +131,7 @@ fun HomeScreen(viewModel: HomeScreenViewModel) {
|
|||||||
listState = listState,
|
listState = listState,
|
||||||
state = state,
|
state = state,
|
||||||
onRetry = { viewModel.refresh() },
|
onRetry = { viewModel.refresh() },
|
||||||
|
onGenreSelected = { viewModel.selectGenre(it) },
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
PullToRefreshBox(
|
PullToRefreshBox(
|
||||||
@ -132,6 +145,7 @@ fun HomeScreen(viewModel: HomeScreenViewModel) {
|
|||||||
listState = listState,
|
listState = listState,
|
||||||
state = state,
|
state = state,
|
||||||
onRetry = { viewModel.refresh() },
|
onRetry = { viewModel.refresh() },
|
||||||
|
onGenreSelected = { viewModel.selectGenre(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +164,7 @@ private fun HomeContent(
|
|||||||
listState: androidx.compose.foundation.lazy.LazyListState,
|
listState: androidx.compose.foundation.lazy.LazyListState,
|
||||||
state: HomeScreenState,
|
state: HomeScreenState,
|
||||||
onRetry: () -> Unit,
|
onRetry: () -> Unit,
|
||||||
|
onGenreSelected: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val shellBottomInset = LocalAppShellBottomInset.current
|
val shellBottomInset = LocalAppShellBottomInset.current
|
||||||
val contentPadding = remember(shellBottomInset) {
|
val contentPadding = remember(shellBottomInset) {
|
||||||
@ -216,13 +231,55 @@ private fun HomeContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items(state.browseSections) { section ->
|
if (state.genres.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
GenreTabs(
|
||||||
|
genres = state.genres,
|
||||||
|
selectedGenreId = state.selectedGenreId,
|
||||||
|
onGenreSelected = onGenreSelected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentSections = state.selectedGenreId?.let { state.browseSections[it] }
|
||||||
|
|
||||||
|
if (currentSections == null) {
|
||||||
|
item {
|
||||||
|
SkeletonTree(true) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(180.dp)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
)
|
||||||
|
LazyRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
|
) {
|
||||||
|
items(4) {
|
||||||
|
PlayableCard(
|
||||||
|
title = "Item Title",
|
||||||
|
subtitle = "Subtitle",
|
||||||
|
imageURL = "https://placehold.co/600x400",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items(currentSections) { section ->
|
||||||
HomeSection(
|
HomeSection(
|
||||||
title = section.title,
|
title = section.title,
|
||||||
subtitle = section.description,
|
subtitle = section.description,
|
||||||
items = section.items,
|
items = section.items,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state is HomeScreenState.Data.LoadingMore) {
|
if (state is HomeScreenState.Data.LoadingMore) {
|
||||||
item {
|
item {
|
||||||
@ -307,3 +364,26 @@ private fun HomeSection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun GenreTabs(
|
||||||
|
genres: List<MetadataBrowseGenre>,
|
||||||
|
selectedGenreId: String?,
|
||||||
|
onGenreSelected: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val rowState = rememberLazyListState()
|
||||||
|
|
||||||
|
LazyRow(
|
||||||
|
state = rowState,
|
||||||
|
modifier = Modifier.dragScrollable(rowState),
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
) {
|
||||||
|
items(genres) { genre ->
|
||||||
|
ChipTab(
|
||||||
|
text = genre.name,
|
||||||
|
selected = selectedGenreId == genre.id,
|
||||||
|
onClick = { onGenreSelected(genre.id) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package dev.krtirtho.spotube.modules.home
|
package dev.krtirtho.spotube.modules.home
|
||||||
|
|
||||||
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseGenre
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult
|
||||||
@ -45,8 +46,10 @@ class HomeScreenRepository(
|
|||||||
get() = pluginManager.selectedMetadataPlugin.value
|
get() = pluginManager.selectedMetadataPlugin.value
|
||||||
|
|
||||||
private val featuredItemCache = Cache.Builder<String, List<MetadataBrowseItem>>().build()
|
private val featuredItemCache = Cache.Builder<String, List<MetadataBrowseItem>>().build()
|
||||||
|
private val genresCache = Cache.Builder<String, List<MetadataBrowseGenre>>().build()
|
||||||
private val browseItemCache =
|
private val browseItemCache =
|
||||||
Cache.Builder<PaginationStrategy, PaginationResult<MetadataBrowseSection>>().build()
|
Cache.Builder<Pair<String, PaginationStrategy>, PaginationResult<MetadataBrowseSection>>()
|
||||||
|
.build()
|
||||||
private val sublistItemCache =
|
private val sublistItemCache =
|
||||||
Cache.Builder<Pair<String, PaginationStrategy>, PaginationResult<MetadataBrowseItem>>()
|
Cache.Builder<Pair<String, PaginationStrategy>, PaginationResult<MetadataBrowseItem>>()
|
||||||
.build()
|
.build()
|
||||||
@ -65,6 +68,7 @@ class HomeScreenRepository(
|
|||||||
|
|
||||||
fun invalidateCaches() {
|
fun invalidateCaches() {
|
||||||
featuredItemCache.invalidateAll()
|
featuredItemCache.invalidateAll()
|
||||||
|
genresCache.invalidateAll()
|
||||||
browseItemCache.invalidateAll()
|
browseItemCache.invalidateAll()
|
||||||
sublistItemCache.invalidateAll()
|
sublistItemCache.invalidateAll()
|
||||||
}
|
}
|
||||||
@ -79,32 +83,43 @@ class HomeScreenRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun list(paginationStrategy: PaginationStrategy? = null) =
|
suspend fun genres() = plugin?.let { plugin ->
|
||||||
|
genresCache.get("genres") {
|
||||||
|
pluginManager.withScope {
|
||||||
|
plugin.use {
|
||||||
|
metadataBrowseAPI.genres()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun list(genreId: String, paginationStrategy: PaginationStrategy? = null) =
|
||||||
plugin?.let { plugin ->
|
plugin?.let { plugin ->
|
||||||
browseItemCache.get(
|
browseItemCache.get(
|
||||||
key = paginationStrategy ?: PaginationStrategy.Offset(0, 20)
|
key = genreId to (paginationStrategy ?: PaginationStrategy.Offset(0, 20))
|
||||||
) {
|
) {
|
||||||
pluginManager.withScope {
|
pluginManager.withScope {
|
||||||
plugin.use {
|
plugin.use {
|
||||||
metadataBrowseAPI.list(paginationStrategy)
|
metadataBrowseAPI.list(genreId, paginationStrategy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sublist(
|
suspend fun sublist(
|
||||||
|
genreId: String,
|
||||||
parentId: String,
|
parentId: String,
|
||||||
paginationStrategy: PaginationStrategy? = null
|
paginationStrategy: PaginationStrategy? = null
|
||||||
) = plugin?.let { plugin ->
|
) = plugin?.let { plugin ->
|
||||||
sublistItemCache.get(
|
sublistItemCache.get(
|
||||||
parentId to (paginationStrategy ?: PaginationStrategy.Offset(
|
"$genreId/$parentId" to (paginationStrategy ?: PaginationStrategy.Offset(
|
||||||
0,
|
0,
|
||||||
20
|
20
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
pluginManager.withScope {
|
pluginManager.withScope {
|
||||||
plugin.use {
|
plugin.use {
|
||||||
metadataBrowseAPI.sublist(parentId, paginationStrategy)
|
metadataBrowseAPI.sublist(genreId, parentId, paginationStrategy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package dev.krtirtho.spotube.modules.home
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseGenre
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult
|
||||||
@ -27,19 +28,11 @@ import dev.krtirtho.spotube.core.di.injectLogger
|
|||||||
import dev.krtirtho.spotube.modules.plugin.PluginManager
|
import dev.krtirtho.spotube.modules.plugin.PluginManager
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
|
||||||
import kotlinx.coroutines.flow.onStart
|
|
||||||
import kotlinx.coroutines.flow.scan
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
|
||||||
@ -48,29 +41,33 @@ sealed interface HomeScreenState {
|
|||||||
|
|
||||||
sealed interface Data : HomeScreenState {
|
sealed interface Data : HomeScreenState {
|
||||||
val featuredItems: List<MetadataBrowseItem>
|
val featuredItems: List<MetadataBrowseItem>
|
||||||
val browseSections: List<MetadataBrowseSection>
|
val genres: List<MetadataBrowseGenre>
|
||||||
val paginationStrategy: PaginationStrategy?
|
val selectedGenreId: String?
|
||||||
val totalItems: Int
|
val browseSections: Map<String, List<MetadataBrowseSection>?>
|
||||||
|
val paginationStrategies: Map<String, PaginationStrategy?>
|
||||||
|
|
||||||
data class Loaded(
|
data class Loaded(
|
||||||
override val featuredItems: List<MetadataBrowseItem>,
|
override val featuredItems: List<MetadataBrowseItem>,
|
||||||
override val browseSections: List<MetadataBrowseSection>,
|
override val genres: List<MetadataBrowseGenre>,
|
||||||
override val paginationStrategy: PaginationStrategy?,
|
override val selectedGenreId: String?,
|
||||||
override val totalItems: Int,
|
override val browseSections: Map<String, List<MetadataBrowseSection>?>,
|
||||||
|
override val paginationStrategies: Map<String, PaginationStrategy?>,
|
||||||
) : Data {
|
) : Data {
|
||||||
fun toLoadingMore(): LoadingMore = LoadingMore(
|
fun toLoadingMore(): LoadingMore = LoadingMore(
|
||||||
featuredItems = featuredItems,
|
featuredItems = featuredItems,
|
||||||
|
genres = genres,
|
||||||
|
selectedGenreId = selectedGenreId,
|
||||||
browseSections = browseSections,
|
browseSections = browseSections,
|
||||||
paginationStrategy = paginationStrategy,
|
paginationStrategies = paginationStrategies,
|
||||||
totalItems = totalItems,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class LoadingMore(
|
data class LoadingMore(
|
||||||
override val featuredItems: List<MetadataBrowseItem>,
|
override val featuredItems: List<MetadataBrowseItem>,
|
||||||
override val browseSections: List<MetadataBrowseSection>,
|
override val genres: List<MetadataBrowseGenre>,
|
||||||
override val paginationStrategy: PaginationStrategy?,
|
override val selectedGenreId: String?,
|
||||||
override val totalItems: Int,
|
override val browseSections: Map<String, List<MetadataBrowseSection>?>,
|
||||||
|
override val paginationStrategies: Map<String, PaginationStrategy?>,
|
||||||
) : Data
|
) : Data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,30 +96,67 @@ class HomeScreenViewModel(
|
|||||||
|
|
||||||
private suspend fun loadInitialData() = runCatching {
|
private suspend fun loadInitialData() = runCatching {
|
||||||
state.value = HomeScreenState.Loading
|
state.value = HomeScreenState.Loading
|
||||||
val featuredItems = repository.featuredItems()
|
val featuredItems = repository.featuredItems() ?: emptyList()
|
||||||
val browseSections = repository.list()
|
val genres = repository.genres() ?: emptyList()
|
||||||
|
val firstGenreId = genres.firstOrNull()?.id
|
||||||
|
val browseSections = mutableMapOf<String, List<MetadataBrowseSection>?>()
|
||||||
|
val paginationStrategies = mutableMapOf<String, PaginationStrategy?>()
|
||||||
|
if (firstGenreId != null) {
|
||||||
|
val result = repository.list(firstGenreId)
|
||||||
|
browseSections[firstGenreId] = result?.items ?: emptyList()
|
||||||
|
paginationStrategies[firstGenreId] = result?.nextPagination
|
||||||
|
}
|
||||||
state.value = HomeScreenState.Data.Loaded(
|
state.value = HomeScreenState.Data.Loaded(
|
||||||
featuredItems = featuredItems ?: emptyList(),
|
featuredItems = featuredItems,
|
||||||
browseSections = browseSections?.items ?: emptyList(),
|
genres = genres,
|
||||||
paginationStrategy = browseSections?.nextPagination,
|
selectedGenreId = firstGenreId,
|
||||||
totalItems = browseSections?.items?.size ?: 0,
|
browseSections = browseSections,
|
||||||
|
paginationStrategies = paginationStrategies,
|
||||||
)
|
)
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
logger.e(e) { "Failed to load home screen data" }
|
logger.e(e) { "Failed to load home screen data" }
|
||||||
state.value = HomeScreenState.Error(e.message ?: "Unknown error")
|
state.value = HomeScreenState.Error(e.message ?: "Unknown error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectGenre(genreId: String) {
|
||||||
|
val currentState = state.value
|
||||||
|
if (currentState is HomeScreenState.Data) {
|
||||||
|
state.value = when (currentState) {
|
||||||
|
is HomeScreenState.Data.Loaded -> currentState.copy(selectedGenreId = genreId)
|
||||||
|
is HomeScreenState.Data.LoadingMore -> currentState.copy(selectedGenreId = genreId)
|
||||||
|
}
|
||||||
|
if (currentState.browseSections[genreId] == null) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val result = repository.list(genreId)
|
||||||
|
val current = state.value
|
||||||
|
if (current is HomeScreenState.Data) {
|
||||||
|
state.value = HomeScreenState.Data.Loaded(
|
||||||
|
featuredItems = current.featuredItems,
|
||||||
|
genres = current.genres,
|
||||||
|
selectedGenreId = genreId,
|
||||||
|
browseSections = current.browseSections + (genreId to (result?.items ?: emptyList())),
|
||||||
|
paginationStrategies = current.paginationStrategies + (genreId to result?.nextPagination),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun loadMoreData() = runCatching {
|
suspend fun loadMoreData() = runCatching {
|
||||||
val currentState = state.value
|
val currentState = state.value
|
||||||
if (currentState is HomeScreenState.Data.Loaded && currentState.paginationStrategy != null) {
|
if (currentState is HomeScreenState.Data.Loaded) {
|
||||||
|
val genreId = currentState.selectedGenreId ?: return@runCatching
|
||||||
|
val pagination = currentState.paginationStrategies[genreId] ?: return@runCatching
|
||||||
state.value = currentState.toLoadingMore()
|
state.value = currentState.toLoadingMore()
|
||||||
val browseSectionsResult = repository.list(currentState.paginationStrategy)
|
val result = repository.list(genreId, pagination)
|
||||||
|
val existingSections = currentState.browseSections[genreId] ?: emptyList()
|
||||||
state.value = HomeScreenState.Data.Loaded(
|
state.value = HomeScreenState.Data.Loaded(
|
||||||
featuredItems = currentState.featuredItems,
|
featuredItems = currentState.featuredItems,
|
||||||
browseSections = currentState.browseSections + (browseSectionsResult?.items
|
genres = currentState.genres,
|
||||||
?: emptyList()),
|
selectedGenreId = genreId,
|
||||||
paginationStrategy = browseSectionsResult?.nextPagination,
|
browseSections = currentState.browseSections + (genreId to (existingSections + (result?.items ?: emptyList()))),
|
||||||
totalItems = currentState.totalItems,
|
paginationStrategies = currentState.paginationStrategies + (genreId to result?.nextPagination),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||||
|
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseAPI
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseAPI
|
||||||
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseGenre
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection
|
||||||
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy
|
import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy
|
||||||
@ -28,11 +29,22 @@ class RealMetadataBrowseAPI : MetadataBrowseAPI {
|
|||||||
return FakeMetadataStore.getFeaturedItems()
|
return FakeMetadataStore.getFeaturedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun list(pagination: PaginationStrategy?): PaginationResult<MetadataBrowseSection> {
|
override suspend fun genres(): List<MetadataBrowseGenre> {
|
||||||
|
return listOf(
|
||||||
|
MetadataBrowseGenre(id = "1", name = "Pop"),
|
||||||
|
MetadataBrowseGenre(id = "2", name = "Rock"),
|
||||||
|
MetadataBrowseGenre(id = "3", name = "Hip-Hop"),
|
||||||
|
MetadataBrowseGenre(id = "4", name = "Jazz"),
|
||||||
|
MetadataBrowseGenre(id = "5", name = "Classical")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun list(genreId: String, pagination: PaginationStrategy?): PaginationResult<MetadataBrowseSection> {
|
||||||
return FakeMetadataStore.paginate(FakeMetadataStore.getBrowseSections(), pagination)
|
return FakeMetadataStore.paginate(FakeMetadataStore.getBrowseSections(), pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sublist(
|
override suspend fun sublist(
|
||||||
|
genreId: String,
|
||||||
sectionId: String,
|
sectionId: String,
|
||||||
pagination: PaginationStrategy?
|
pagination: PaginationStrategy?
|
||||||
): PaginationResult<MetadataBrowseItem> {
|
): PaginationResult<MetadataBrowseItem> {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const val MetadataBrowseAPI_SERVICE_NAME = "MetadataBrowseAPI"
|
|||||||
|
|
||||||
interface MetadataBrowseAPI: ZiplineService {
|
interface MetadataBrowseAPI: ZiplineService {
|
||||||
suspend fun featured(): List<MetadataBrowseItem>
|
suspend fun featured(): List<MetadataBrowseItem>
|
||||||
suspend fun list(pagination: PaginationStrategy? = null): PaginationResult<MetadataBrowseSection>
|
suspend fun genres(): List<MetadataBrowseGenre>
|
||||||
suspend fun sublist(sectionId: String, pagination: PaginationStrategy? = null): PaginationResult<MetadataBrowseItem>
|
suspend fun list(genreId: String, pagination: PaginationStrategy? = null): PaginationResult<MetadataBrowseSection>
|
||||||
|
suspend fun sublist(genreId: String, sectionId: String, pagination: PaginationStrategy? = null): PaginationResult<MetadataBrowseItem>
|
||||||
}
|
}
|
||||||
@ -54,3 +54,9 @@ data class MetadataBrowseSection(
|
|||||||
val items: List<MetadataBrowseItem>,
|
val items: List<MetadataBrowseItem>,
|
||||||
val moreLink: String? = null
|
val moreLink: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MetadataBrowseGenre(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user