mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-08 12:42:44 -05:00
fix up some season loading stuff
This commit is contained in:
@@ -64,7 +64,7 @@ interface MoviesApi {
|
|||||||
suspend fun getExternalIds(@Path("id") id: Int): Response<ExternalIds>
|
suspend fun getExternalIds(@Path("id") id: Int): Response<ExternalIds>
|
||||||
|
|
||||||
@GET("movie/{id}/account_states")
|
@GET("movie/{id}/account_states")
|
||||||
suspend fun getAccountStates(@Path("id") id: Int, @Query("session_id") sessionId: String): Response<AccountStates>
|
suspend fun getAccountStates(@Path("id") id: Int): Response<AccountStates>
|
||||||
|
|
||||||
@GET("discover/movie")
|
@GET("discover/movie")
|
||||||
suspend fun discover(@Query("with_keywords") keywords: String? = null, @Query("page") page: Int): Response<SearchResult<SearchResultMovie>>
|
suspend fun discover(@Query("with_keywords") keywords: String? = null, @Query("page") page: Int): Response<SearchResult<SearchResultMovie>>
|
||||||
|
|||||||
@@ -148,9 +148,8 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAccountStates(id: Int) {
|
override suspend fun getAccountStates(id: Int) {
|
||||||
val sessionId = SessionManager.currentSession.value?.sessionId ?: throw Exception("Session must not be null")
|
|
||||||
loadRemoteData(
|
loadRemoteData(
|
||||||
{ movieService.getAccountStates(id, sessionId) },
|
{ movieService.getAccountStates(id) },
|
||||||
{ accountStates[id] = it },
|
{ accountStates[id] = it },
|
||||||
accountStatesLoadingState,
|
accountStatesLoadingState,
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -145,7 +145,9 @@ private fun fetchData(
|
|||||||
scope.launch { mainViewModel.getVideos(itemId, type, force) }
|
scope.launch { mainViewModel.getVideos(itemId, type, force) }
|
||||||
scope.launch { mainViewModel.getWatchProviders(itemId, type, force) }
|
scope.launch { mainViewModel.getWatchProviders(itemId, type, force) }
|
||||||
scope.launch { mainViewModel.getReviews(itemId, type, force) }
|
scope.launch { mainViewModel.getReviews(itemId, type, force) }
|
||||||
scope.launch { mainViewModel.getAccountStates(itemId, type) }
|
if (SessionManager.isLoggedIn) {
|
||||||
|
scope.launch { mainViewModel.getAccountStates(itemId, type) }
|
||||||
|
}
|
||||||
|
|
||||||
when (type) {
|
when (type) {
|
||||||
MediaViewType.MOVIE -> {
|
MediaViewType.MOVIE -> {
|
||||||
@@ -190,6 +192,9 @@ fun MediaDetailScreen(
|
|||||||
if (type == MediaViewType.TV) {
|
if (type == MediaViewType.TV) {
|
||||||
LaunchedEffect(mediaItem) {
|
LaunchedEffect(mediaItem) {
|
||||||
val lastSeason = (mediaItem as DetailedTv?)?.numberOfSeasons ?: 0
|
val lastSeason = (mediaItem as DetailedTv?)?.numberOfSeasons ?: 0
|
||||||
|
// for (i in lastSeason downTo 0) {
|
||||||
|
// mainViewModel.getSeason(itemId, i)
|
||||||
|
// }
|
||||||
if (lastSeason > 0) {
|
if (lastSeason > 0) {
|
||||||
mainViewModel.getSeason(itemId, lastSeason)
|
mainViewModel.getSeason(itemId, lastSeason)
|
||||||
}
|
}
|
||||||
@@ -871,7 +876,7 @@ private fun SeasonCard(
|
|||||||
appNavController: NavController
|
appNavController: NavController
|
||||||
) {
|
) {
|
||||||
val seasonsMap = remember { mainViewModel.tvSeasons }
|
val seasonsMap = remember { mainViewModel.tvSeasons }
|
||||||
val lastSeason = seasonsMap[itemId]?.lastOrNull()
|
val lastSeason = seasonsMap[itemId]?.maxByOrNull { it.seasonNumber }
|
||||||
|
|
||||||
lastSeason?.let {
|
lastSeason?.let {
|
||||||
ContentCard(
|
ContentCard(
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.LinearEasing
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -9,17 +14,26 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.ExpandMore
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -29,6 +43,7 @@ import com.google.accompanist.pager.ExperimentalPagerApi
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Episode
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Episode
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Season
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Season
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SeasonAccountStates
|
||||||
import com.owenlejeune.tvtime.extensions.toCompositeParts
|
import com.owenlejeune.tvtime.extensions.toCompositeParts
|
||||||
import com.owenlejeune.tvtime.ui.components.BackButton
|
import com.owenlejeune.tvtime.ui.components.BackButton
|
||||||
import com.owenlejeune.tvtime.ui.components.CastCrewCard
|
import com.owenlejeune.tvtime.ui.components.CastCrewCard
|
||||||
@@ -42,6 +57,7 @@ import com.owenlejeune.tvtime.ui.components.WatchProvidersCard
|
|||||||
import com.owenlejeune.tvtime.ui.theme.Typography
|
import com.owenlejeune.tvtime.ui.theme.Typography
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.ApplicationViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.ApplicationViewModel
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
||||||
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -55,7 +71,9 @@ private fun fetchData(
|
|||||||
) {
|
) {
|
||||||
val scope = CoroutineScope(Dispatchers.IO)
|
val scope = CoroutineScope(Dispatchers.IO)
|
||||||
scope.launch { mainViewModel.getSeason(seriesId, seasonNumber, force) }
|
scope.launch { mainViewModel.getSeason(seriesId, seasonNumber, force) }
|
||||||
scope.launch { mainViewModel.getSeasonAccountStates(seriesId, seasonNumber, force) }
|
if (SessionManager.isLoggedIn) {
|
||||||
|
scope.launch { mainViewModel.getSeasonAccountStates(seriesId, seasonNumber, force) }
|
||||||
|
}
|
||||||
scope.launch { mainViewModel.getSeasonImages(seriesId, seasonNumber, force) }
|
scope.launch { mainViewModel.getSeasonImages(seriesId, seasonNumber, force) }
|
||||||
scope.launch { mainViewModel.getSeasonVideos(seriesId, seasonNumber, force) }
|
scope.launch { mainViewModel.getSeasonVideos(seriesId, seasonNumber, force) }
|
||||||
scope.launch { mainViewModel.getSeasonCredits(seriesId, seasonNumber, force) }
|
scope.launch { mainViewModel.getSeasonCredits(seriesId, seasonNumber, force) }
|
||||||
@@ -126,26 +144,61 @@ private fun SeasonContent(
|
|||||||
expandedPosterAsBackdrop = true
|
expandedPosterAsBackdrop = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var isExpanded by remember { mutableStateOf(true) }
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 16.dp, end = 16.dp),
|
.padding(start = 16.dp, end = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Row {
|
||||||
text = season.name,
|
Text(
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
text = season.name,
|
||||||
style = Typography.headlineLarge,
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
maxLines = 2,
|
style = Typography.headlineLarge,
|
||||||
overflow = TextOverflow.Ellipsis,
|
maxLines = 2,
|
||||||
modifier = Modifier.fillMaxWidth()
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
var currentRotation by remember { mutableFloatStateOf(180f) }
|
||||||
|
val rotation = remember { Animatable(currentRotation) }
|
||||||
|
LaunchedEffect(isExpanded) {
|
||||||
|
rotation.animateTo(
|
||||||
|
targetValue = if (isExpanded) 180f else 0f,
|
||||||
|
animationSpec = tween(200, easing = LinearEasing)
|
||||||
|
) {
|
||||||
|
currentRotation = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ExpandMore,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.rotate(currentRotation)
|
||||||
|
.clickable {
|
||||||
|
isExpanded = !isExpanded
|
||||||
|
},
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val accountStatesMap = remember { mainViewModel.tvSeasonAccountStates }
|
val accountStatesMap = remember { mainViewModel.tvSeasonAccountStates }
|
||||||
val accountStates = accountStatesMap[seriesId]?.get(season.seasonNumber)
|
val accountStates = accountStatesMap[seriesId]?.get(season.seasonNumber)
|
||||||
|
|
||||||
season.episodes.forEach { episode ->
|
AnimatedVisibility(
|
||||||
val rating = accountStates?.results?.find { it.id == episode.id }?.takeUnless { !it.isRated }?.rating
|
visible = isExpanded
|
||||||
SeasonEpisodeItem(appNavController = appNavController, episode = episode, rating = rating)
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
season.episodes.forEach { episode ->
|
||||||
|
DrawEpisodeCard(
|
||||||
|
episode = episode,
|
||||||
|
accountStates = accountStates,
|
||||||
|
appNavController = appNavController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val imagesMap = remember { mainViewModel.tvSeasonImages }
|
val imagesMap = remember { mainViewModel.tvSeasonImages }
|
||||||
@@ -173,6 +226,16 @@ private fun SeasonContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DrawEpisodeCard(
|
||||||
|
episode: Episode,
|
||||||
|
accountStates: SeasonAccountStates?,
|
||||||
|
appNavController: NavController
|
||||||
|
) {
|
||||||
|
val rating = accountStates?.results?.find { it.id == episode.id }?.takeUnless { !it.isRated }?.rating
|
||||||
|
SeasonEpisodeItem(appNavController = appNavController, episode = episode, rating = rating)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SeasonEpisodeItem(
|
private fun SeasonEpisodeItem(
|
||||||
appNavController: NavController,
|
appNavController: NavController,
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ fun SeasonListScreen(
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
seriesId = id,
|
seriesId = id,
|
||||||
season = season,
|
season = season,
|
||||||
expandedByDefault = index == 0
|
expandedByDefault = index == 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ object SessionManager: KoinComponent {
|
|||||||
|
|
||||||
val currentSession = mutableStateOf<Session?>(null)
|
val currentSession = mutableStateOf<Session?>(null)
|
||||||
|
|
||||||
|
val isLoggedIn: Boolean
|
||||||
|
get() = currentSession.value?.isAuthorized == true
|
||||||
|
|
||||||
class AuthorizedSessionValues(
|
class AuthorizedSessionValues(
|
||||||
@SerializedName("session_id") val sessionId: String,
|
@SerializedName("session_id") val sessionId: String,
|
||||||
@SerializedName("access_token") val accessToken: String,
|
@SerializedName("access_token") val accessToken: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user