mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Add more to discover tab
This commit is contained in:
parent
f43ee6b36c
commit
27dc031012
5 changed files with 140 additions and 8 deletions
|
|
@ -50,6 +50,27 @@ class SeerrService
|
||||||
?.map(::DiscoverItem)
|
?.map(::DiscoverItem)
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
|
||||||
|
suspend fun trending(page: Int = 1): List<DiscoverItem> =
|
||||||
|
api.searchApi
|
||||||
|
.discoverTrendingGet(page = page)
|
||||||
|
.results
|
||||||
|
?.map(::DiscoverItem)
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
|
suspend fun upcomingMovies(page: Int = 1): List<DiscoverItem> =
|
||||||
|
api.searchApi
|
||||||
|
.discoverMoviesUpcomingGet(page = page)
|
||||||
|
.results
|
||||||
|
?.map(::DiscoverItem)
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
|
suspend fun upcomingTv(page: Int = 1): List<DiscoverItem> =
|
||||||
|
api.searchApi
|
||||||
|
.discoverTvUpcomingGet(page = page)
|
||||||
|
.results
|
||||||
|
?.map(::DiscoverItem)
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get [DiscoverItem]s similar to the JF items such as movies, series, or people
|
* Get [DiscoverItem]s similar to the JF items such as movies, series, or people
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.detail.discover
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.DiscoverRating
|
||||||
|
import com.github.damontecres.wholphin.ui.components.DotSeparatedRow
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DiscoverQuickDetails(
|
||||||
|
item: DiscoverItem?,
|
||||||
|
rating: DiscoverRating?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Timber.v("id=${item?.id}, rating=$rating")
|
||||||
|
val context = LocalContext.current
|
||||||
|
val details =
|
||||||
|
remember(item) {
|
||||||
|
buildList {
|
||||||
|
item
|
||||||
|
?.releaseDate
|
||||||
|
?.year
|
||||||
|
?.toString()
|
||||||
|
?.let(::add)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DotSeparatedRow(
|
||||||
|
texts = details,
|
||||||
|
communityRating = rating?.audienceRating,
|
||||||
|
criticRating = rating?.criticRating?.toFloat(),
|
||||||
|
textStyle = MaterialTheme.typography.titleSmall,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,8 @@ import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.DiscoverRating
|
||||||
|
import com.github.damontecres.wholphin.data.model.SeerrItemType
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
|
@ -40,11 +42,13 @@ import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.discover.DiscoverQuickDetails
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageHeader
|
import com.github.damontecres.wholphin.ui.main.HomePageHeader
|
||||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.DataLoadingState
|
import com.github.damontecres.wholphin.util.DataLoadingState
|
||||||
|
import com.google.common.cache.CacheBuilder
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -63,6 +67,7 @@ class SeerrDiscoverViewModel
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val state = MutableStateFlow<DiscoverState>(DiscoverState())
|
val state = MutableStateFlow<DiscoverState>(DiscoverState())
|
||||||
|
val rating = MutableStateFlow<Map<Int, DiscoverRating>>(mapOf())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
|
|
@ -74,6 +79,21 @@ class SeerrDiscoverViewModel
|
||||||
fetchAndUpdateState(seerrService::discoverTv) {
|
fetchAndUpdateState(seerrService::discoverTv) {
|
||||||
this.copy(tv = DiscoverRowData(context.getString(R.string.tv_shows), it))
|
this.copy(tv = DiscoverRowData(context.getString(R.string.tv_shows), it))
|
||||||
}
|
}
|
||||||
|
fetchAndUpdateState(seerrService::trending) {
|
||||||
|
this.copy(trending = DiscoverRowData(context.getString(R.string.trending), it))
|
||||||
|
}
|
||||||
|
fetchAndUpdateState(seerrService::upcomingMovies) {
|
||||||
|
this.copy(
|
||||||
|
upcomingMovies =
|
||||||
|
DiscoverRowData(context.getString(R.string.upcoming_movies), it),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fetchAndUpdateState(seerrService::upcomingTv) {
|
||||||
|
this.copy(
|
||||||
|
upcomingTv =
|
||||||
|
DiscoverRowData(context.getString(R.string.upcoming_tv), it),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchAndUpdateState(
|
private fun fetchAndUpdateState(
|
||||||
|
|
@ -101,6 +121,49 @@ class SeerrDiscoverViewModel
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
backdropService.submit("discover_${item.id}", item.backDropUrl)
|
backdropService.submit("discover_${item.id}", item.backDropUrl)
|
||||||
|
fetchRating(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ratingCache =
|
||||||
|
CacheBuilder
|
||||||
|
.newBuilder()
|
||||||
|
.maximumSize(100)
|
||||||
|
.build<Int, DiscoverRating>()
|
||||||
|
|
||||||
|
// TODO this is not very efficient
|
||||||
|
fun fetchRating(item: DiscoverItem) {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
val cachedResult = ratingCache.getIfPresent(item.id)
|
||||||
|
if (cachedResult != null) {
|
||||||
|
return@launchIO
|
||||||
|
}
|
||||||
|
val result =
|
||||||
|
when (item.type) {
|
||||||
|
SeerrItemType.MOVIE -> {
|
||||||
|
DiscoverRating(
|
||||||
|
seerrService.api.moviesApi.movieMovieIdRatingsGet(
|
||||||
|
movieId = item.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.TV -> {
|
||||||
|
DiscoverRating(seerrService.api.tvApi.tvTvIdRatingsGet(tvId = item.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.PERSON -> {
|
||||||
|
DiscoverRating(null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrItemType.UNKNOWN -> {
|
||||||
|
DiscoverRating(null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ratingCache.put(item.id, result)
|
||||||
|
rating.update {
|
||||||
|
ratingCache.asMap().toMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,6 +181,9 @@ data class DiscoverRowData(
|
||||||
data class DiscoverState(
|
data class DiscoverState(
|
||||||
val movies: DiscoverRowData = DiscoverRowData.EMPTY,
|
val movies: DiscoverRowData = DiscoverRowData.EMPTY,
|
||||||
val tv: DiscoverRowData = DiscoverRowData.EMPTY,
|
val tv: DiscoverRowData = DiscoverRowData.EMPTY,
|
||||||
|
val trending: DiscoverRowData = DiscoverRowData.EMPTY,
|
||||||
|
val upcomingMovies: DiscoverRowData = DiscoverRowData.EMPTY,
|
||||||
|
val upcomingTv: DiscoverRowData = DiscoverRowData.EMPTY,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -127,7 +193,9 @@ fun SeerrDiscoverPage(
|
||||||
viewModel: SeerrDiscoverViewModel = hiltViewModel(),
|
viewModel: SeerrDiscoverViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
val rows = listOf(state.movies, state.tv)
|
val rows =
|
||||||
|
listOf(state.trending, state.movies, state.tv, state.upcomingMovies, state.upcomingTv)
|
||||||
|
val ratingMap by viewModel.rating.collectAsState()
|
||||||
|
|
||||||
val focusRequesters = remember(2) { List(rows.size) { FocusRequester() } }
|
val focusRequesters = remember(2) { List(rows.size) { FocusRequester() } }
|
||||||
var position by rememberPosition(0, -1)
|
var position by rememberPosition(0, -1)
|
||||||
|
|
@ -141,8 +209,8 @@ fun SeerrDiscoverPage(
|
||||||
viewModel.updateBackdrop(focusedItem)
|
viewModel.updateBackdrop(focusedItem)
|
||||||
}
|
}
|
||||||
var firstFocused by rememberSaveable { mutableStateOf(false) }
|
var firstFocused by rememberSaveable { mutableStateOf(false) }
|
||||||
LaunchedEffect(state.movies) {
|
LaunchedEffect(state.trending) {
|
||||||
if (!firstFocused && state.movies.items is DataLoadingState.Success<*>) {
|
if (!firstFocused && state.trending.items is DataLoadingState.Success<*>) {
|
||||||
firstFocused = focusRequesters.getOrNull(0)?.tryRequestFocus("discover") == true
|
firstFocused = focusRequesters.getOrNull(0)?.tryRequestFocus("discover") == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,12 +224,15 @@ fun SeerrDiscoverPage(
|
||||||
overview = focusedItem?.overview,
|
overview = focusedItem?.overview,
|
||||||
overviewTwoLines = true,
|
overviewTwoLines = true,
|
||||||
quickDetails = {
|
quickDetails = {
|
||||||
// TODO
|
DiscoverQuickDetails(
|
||||||
|
item = focusedItem,
|
||||||
|
rating = focusedItem?.id?.let { ratingMap[it] },
|
||||||
|
)
|
||||||
},
|
},
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight(.33f)
|
.padding(top = 24.dp, bottom = 16.dp, start = 32.dp)
|
||||||
.padding(top = 48.dp, bottom = 32.dp, start = 32.dp),
|
.fillMaxHeight(.25f),
|
||||||
)
|
)
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
|
|
||||||
|
|
@ -640,8 +640,7 @@ fun NavigationDrawerScope.NavItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
NavDrawerItem.Discover -> {
|
NavDrawerItem.Discover -> {
|
||||||
// TODO seerr
|
R.string.fa_magnifying_glass_plus
|
||||||
R.string.fa_list_ul
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServerNavDrawerItem -> {
|
is ServerNavDrawerItem -> {
|
||||||
|
|
|
||||||
|
|
@ -428,6 +428,9 @@
|
||||||
<string name="password">Password</string>
|
<string name="password">Password</string>
|
||||||
<string name="username">Username</string>
|
<string name="username">Username</string>
|
||||||
<string name="url">URL</string>
|
<string name="url">URL</string>
|
||||||
|
<string name="trending">Trending</string>
|
||||||
|
<string name="upcoming_movies">Upcoming Movies</string>
|
||||||
|
<string name="upcoming_tv">Upcoming TV Shows</string>
|
||||||
|
|
||||||
<string-array name="theme_song_volume">
|
<string-array name="theme_song_volume">
|
||||||
<item>Disabled</item>
|
<item>Disabled</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue