mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-11 14:22:55 -05:00
add trending content to home pages
This commit is contained in:
@@ -53,7 +53,7 @@ class BasePagingSource<T: Any, S>(
|
|||||||
nextKey = if (results.isEmpty()) { null } else { page + 1}
|
nextKey = if (results.isEmpty()) { null } else { page + 1}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, context.getString(R.string.no_result_found), Toast.LENGTH_SHORT).show()
|
// Toast.makeText(context, context.getString(R.string.no_result_found), Toast.LENGTH_SHORT).show()
|
||||||
LoadResult.Invalid()
|
LoadResult.Invalid()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
@@ -67,4 +68,7 @@ interface MoviesApi {
|
|||||||
@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>>
|
||||||
|
|
||||||
|
@GET("trending/movie/{time_window}")
|
||||||
|
suspend fun trending(@Path("time_window") timeWindow: String, @Query("page") page: Int): Response<SearchResult<SearchResultMovie>>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Video
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Video
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
@@ -137,6 +138,10 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getTrending(timeWindow: TimeWindow, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||||
|
return movieService.trending(timeWindow.name.lowercase(), page)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||||
return movieService.discover(keywords, page)
|
return movieService.discover(keywords, page)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ExternalIds
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePeopleResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePeopleResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImageCollection
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResult
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultPerson
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
@@ -36,4 +39,7 @@ interface PeopleApi {
|
|||||||
@GET("person/{id}/external_ids")
|
@GET("person/{id}/external_ids")
|
||||||
suspend fun getExternalIds(@Path("id") id: Int): Response<ExternalIds>
|
suspend fun getExternalIds(@Path("id") id: Int): Response<ExternalIds>
|
||||||
|
|
||||||
|
@GET("trending/person/{time_window}")
|
||||||
|
suspend fun trending(@Path("time_window") timeWindow: String, @Query("page") page: Int): Response<SearchResult<SearchResultPerson>>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,10 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePeopleResponse
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImage
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImage
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonImageCollection
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResult
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultMedia
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultPerson
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import okhttp3.internal.notify
|
import okhttp3.internal.notify
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@@ -90,4 +94,8 @@ class PeopleService: KoinComponent {
|
|||||||
return service.getPopular(page)
|
return service.getPopular(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getTrending(timeWindow: TimeWindow, page: Int): Response<SearchResult<SearchResultPerson>> {
|
||||||
|
return service.trending(timeWindow.name.lowercase(), page)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
@@ -69,4 +70,7 @@ interface TvApi {
|
|||||||
|
|
||||||
@GET("discover/tv")
|
@GET("discover/tv")
|
||||||
suspend fun discover(@Query("page") page: Int, @Query("with_keywords") keywords: String? = null): Response<SearchResult<SearchResultTv>>
|
suspend fun discover(@Query("page") page: Int, @Query("with_keywords") keywords: String? = null): Response<SearchResult<SearchResultTv>>
|
||||||
|
|
||||||
|
@GET("trending/tv/{time_window}")
|
||||||
|
suspend fun trending(@Path("time_window") timeWindow: String, @Query("page") page: Int): Response<SearchResult<SearchResultTv>>
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviderResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviderResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
@@ -140,6 +141,10 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getTrending(timeWindow: TimeWindow, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||||
|
return service.trending(timeWindow.name.lowercase(), page)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||||
return service.discover(page, keywords)
|
return service.discover(page, keywords)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ abstract class SearchResultMedia(
|
|||||||
@SerializedName("genre_ids") val genreIds: List<Int>,
|
@SerializedName("genre_ids") val genreIds: List<Int>,
|
||||||
@SerializedName("original_language") val originalLanguage: String,
|
@SerializedName("original_language") val originalLanguage: String,
|
||||||
@SerializedName("original_name", alternate = ["original_title"]) val originalName: String,
|
@SerializedName("original_name", alternate = ["original_title"]) val originalName: String,
|
||||||
@SerializedName("poster_path") val posterPath: String?,
|
posterPath: String?,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
id: Int,
|
id: Int,
|
||||||
name: String,
|
name: String,
|
||||||
popularity: Float
|
popularity: Float
|
||||||
): SortableSearchResult(type, popularity, id, name)
|
): SortableSearchResult(type, popularity, id, name, posterPath)
|
||||||
@@ -4,10 +4,10 @@ import com.google.gson.annotations.SerializedName
|
|||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class SearchResultPerson(
|
class SearchResultPerson(
|
||||||
@SerializedName("profile_path") val profilePath: String,
|
|
||||||
@SerializedName("adult") val isAdult: Boolean,
|
@SerializedName("adult") val isAdult: Boolean,
|
||||||
@SerializedName("known_for") val knownFor: List<KnownFor>,
|
@SerializedName("known_for") val knownFor: List<KnownFor>,
|
||||||
|
profilePath: String,
|
||||||
id: Int,
|
id: Int,
|
||||||
name: String,
|
name: String,
|
||||||
popularity: Float
|
popularity: Float
|
||||||
): SortableSearchResult(MediaViewType.PERSON, popularity, id, name)
|
): SortableSearchResult(MediaViewType.PERSON, popularity, id, name, profilePath)
|
||||||
@@ -6,6 +6,7 @@ import com.owenlejeune.tvtime.utils.types.MediaViewType
|
|||||||
abstract class SortableSearchResult(
|
abstract class SortableSearchResult(
|
||||||
@SerializedName("media_type") val mediaType: MediaViewType,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
@SerializedName("popularity") val popularity: Float,
|
@SerializedName("popularity") val popularity: Float,
|
||||||
@SerializedName("id") val id: Int,
|
id: Int,
|
||||||
@SerializedName("name", alternate = ["title"]) val name: String
|
name: String,
|
||||||
): Searchable
|
posterPath: String?
|
||||||
|
): TmdbItem(id, posterPath, name), Searchable
|
||||||
@@ -4,6 +4,6 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
abstract class TmdbItem(
|
abstract class TmdbItem(
|
||||||
@SerializedName("id") val id: Int,
|
@SerializedName("id") val id: Int,
|
||||||
@SerializedName("poster_path") val posterPath: String?,
|
@SerializedName("poster_path", alternate = ["profile_path"]) val posterPath: String?,
|
||||||
@SerializedName("name", alternate = ["title"]) val title: String
|
@SerializedName("name", alternate = ["title"]) val title: String
|
||||||
)
|
)
|
||||||
@@ -52,11 +52,13 @@ private val POSTER_HEIGHT = 180.dp
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PagingPosterGrid(
|
fun PagingPosterGrid(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
lazyPagingItems: LazyPagingItems<TmdbItem>?,
|
lazyPagingItems: LazyPagingItems<TmdbItem>?,
|
||||||
onClick: (id: Int) -> Unit = {}
|
onClick: (id: Int) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
lazyPagingItems?.let {
|
lazyPagingItems?.let {
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
|
modifier = modifier,
|
||||||
columns = GridCells.Adaptive(minSize = POSTER_WIDTH),
|
columns = GridCells.Adaptive(minSize = POSTER_WIDTH),
|
||||||
contentPadding = PaddingValues(8.dp),
|
contentPadding = PaddingValues(8.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
|||||||
@@ -1117,15 +1117,18 @@ fun SelectableTextChip(
|
|||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onSelected: () -> Unit,
|
onSelected: () -> Unit,
|
||||||
text: String,
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
selectedColor: Color = MaterialTheme.colorScheme.secondary,
|
selectedColor: Color = MaterialTheme.colorScheme.secondary,
|
||||||
unselectedColor: Color = MaterialTheme.colorScheme.surfaceVariant
|
unselectedColor: Color = MaterialTheme.colorScheme.surfaceVariant
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier.then(
|
||||||
.clip(RoundedCornerShape(percent = 25))
|
Modifier
|
||||||
.border(width = 1.dp, color = selectedColor, shape = RoundedCornerShape(percent = 25))
|
.clip(RoundedCornerShape(percent = 25))
|
||||||
.background(color = if(selected) selectedColor else unselectedColor)
|
.border(width = 1.dp, color = selectedColor, shape = RoundedCornerShape(percent = 25))
|
||||||
.clickable(onClick = onSelected)
|
.background(color = if(selected) selectedColor else unselectedColor)
|
||||||
|
.clickable(onClick = onSelected)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ import com.owenlejeune.tvtime.ui.components.AccountIcon
|
|||||||
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.tabs.AccountTabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.AccountViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.AccountViewModel
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fun KeywordResultsScreen(
|
|||||||
id = result.id,
|
id = result.id,
|
||||||
backdropPath = TmdbUtils.getFullBackdropPath(result.backdropPath),
|
backdropPath = TmdbUtils.getFullBackdropPath(result.backdropPath),
|
||||||
posterPath = TmdbUtils.getFullPosterPath(result.posterPath),
|
posterPath = TmdbUtils.getFullPosterPath(result.posterPath),
|
||||||
title = result.name,
|
title = result.title,
|
||||||
additionalDetails = listOf(result.overview)
|
additionalDetails = listOf(result.overview)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,17 +204,19 @@ fun PersonDetailScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun BiographyCard(person: DetailPerson?) {
|
private fun BiographyCard(person: DetailPerson?) {
|
||||||
ExpandableContentCard { isExpanded ->
|
if (person != null && person.biography.isNotEmpty()) {
|
||||||
Text(
|
ExpandableContentCard { isExpanded ->
|
||||||
modifier = Modifier
|
Text(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.wrapContentHeight()
|
.fillMaxWidth()
|
||||||
.padding(top = 12.dp, start = 16.dp, end = 16.dp),
|
.wrapContentHeight()
|
||||||
text = person?.biography ?: "",
|
.padding(top = 12.dp, start = 16.dp, end = 16.dp),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
text = person.biography,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
maxLines = if (isExpanded) Int.MAX_VALUE else 3,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
overflow = TextOverflow.Ellipsis
|
maxLines = if (isExpanded) Int.MAX_VALUE else 3,
|
||||||
)
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,22 +112,22 @@ fun SearchScreen(
|
|||||||
SelectableTextChip(
|
SelectableTextChip(
|
||||||
selected = viewType.value == MediaViewType.MOVIE,
|
selected = viewType.value == MediaViewType.MOVIE,
|
||||||
onSelected = { viewType.value = MediaViewType.MOVIE },
|
onSelected = { viewType.value = MediaViewType.MOVIE },
|
||||||
text = "Movies"
|
text = stringResource(id = R.string.nav_movies_title)
|
||||||
)
|
)
|
||||||
SelectableTextChip(
|
SelectableTextChip(
|
||||||
selected = viewType.value == MediaViewType.TV,
|
selected = viewType.value == MediaViewType.TV,
|
||||||
onSelected = { viewType.value = MediaViewType.TV },
|
onSelected = { viewType.value = MediaViewType.TV },
|
||||||
text = "TV"
|
text = stringResource(id = R.string.nav_tv_title)
|
||||||
)
|
)
|
||||||
SelectableTextChip(
|
SelectableTextChip(
|
||||||
selected = viewType.value == MediaViewType.PERSON,
|
selected = viewType.value == MediaViewType.PERSON,
|
||||||
onSelected = { viewType.value = MediaViewType.PERSON },
|
onSelected = { viewType.value = MediaViewType.PERSON },
|
||||||
text = "People"
|
text = stringResource(id = R.string.nav_people_title)
|
||||||
)
|
)
|
||||||
SelectableTextChip(
|
SelectableTextChip(
|
||||||
selected = viewType.value == MediaViewType.MIXED,
|
selected = viewType.value == MediaViewType.MIXED,
|
||||||
onSelected = { viewType.value = MediaViewType.MIXED },
|
onSelected = { viewType.value = MediaViewType.MIXED },
|
||||||
text = "Multi"
|
text = stringResource(id = R.string.search_multi_title)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +374,7 @@ private fun <T: SortableSearchResult> SearchResultItemView(
|
|||||||
id = searchResult.id,
|
id = searchResult.id,
|
||||||
backdropPath = backdropModel(searchResult),
|
backdropPath = backdropModel(searchResult),
|
||||||
posterPath = posterModel(searchResult),
|
posterPath = posterModel(searchResult),
|
||||||
title = searchResult.name,
|
title = searchResult.title,
|
||||||
additionalDetails = additionalDetails(searchResult)
|
additionalDetails = additionalDetails(searchResult)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -453,7 +453,7 @@ private fun PeopleSearchResultView(
|
|||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
mediaViewType = MediaViewType.PERSON,
|
mediaViewType = MediaViewType.PERSON,
|
||||||
searchResult = result,
|
searchResult = result,
|
||||||
posterModel = { TmdbUtils.getFullPersonImagePath(result.profilePath) },
|
posterModel = { TmdbUtils.getFullPersonImagePath(result.posterPath) },
|
||||||
backdropModel = { TmdbUtils.getFullBackdropPath(mostKnownFor?.backdropPath) },
|
backdropModel = { TmdbUtils.getFullBackdropPath(mostKnownFor?.backdropPath) },
|
||||||
additionalDetails = { additional }
|
additionalDetails = { additional }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@@ -84,7 +84,7 @@ sealed class AccountTabNavItem(
|
|||||||
R.string.no_favorite_movies,
|
R.string.no_favorite_movies,
|
||||||
MediaViewType.MOVIE,
|
MediaViewType.MOVIE,
|
||||||
screenContent,
|
screenContent,
|
||||||
AccountTabType.FAVORITE,
|
AccountTabType.FAVORITE,
|
||||||
FavoriteMovie::class,
|
FavoriteMovie::class,
|
||||||
3
|
3
|
||||||
)
|
)
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.tabs
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
@@ -13,13 +21,14 @@ import com.google.accompanist.pager.PagerState
|
|||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
||||||
import com.owenlejeune.tvtime.ui.components.SearchView
|
import com.owenlejeune.tvtime.ui.components.SearchView
|
||||||
import com.owenlejeune.tvtime.ui.components.Tabs
|
import com.owenlejeune.tvtime.ui.components.SelectableTextChip
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.HomeScreenViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.HomeScreenViewModel
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -44,7 +53,7 @@ fun MediaTab(
|
|||||||
|
|
||||||
val tabs = MediaTabNavItem.itemsForType(type = mediaType)
|
val tabs = MediaTabNavItem.itemsForType(type = mediaType)
|
||||||
val pagerState = rememberPagerState()
|
val pagerState = rememberPagerState()
|
||||||
Tabs(tabs = tabs, pagerState = pagerState)
|
ScrollableTabs(tabs = tabs, pagerState = pagerState)
|
||||||
MediaTabs(
|
MediaTabs(
|
||||||
tabs = tabs,
|
tabs = tabs,
|
||||||
pagerState = pagerState,
|
pagerState = pagerState,
|
||||||
@@ -73,6 +82,52 @@ fun MediaTabContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MediaTabTrendingContent(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
mediaType: MediaViewType,
|
||||||
|
mediaTabItem: MediaTabNavItem
|
||||||
|
) {
|
||||||
|
val viewModel = viewModel<MainViewModel>()
|
||||||
|
|
||||||
|
val timeWindow = remember { mutableStateOf(TimeWindow.DAY) }
|
||||||
|
val flow = remember { mutableStateOf(viewModel.produceTrendingFor(mediaType, timeWindow.value)) }
|
||||||
|
|
||||||
|
LaunchedEffect(timeWindow.value) {
|
||||||
|
flow.value = viewModel.produceTrendingFor(mediaType, timeWindow.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mediaListItems = flow.value.collectAsLazyPagingItems()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
modifier = Modifier.padding(all = 12.dp)
|
||||||
|
) {
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = timeWindow.value == TimeWindow.DAY,
|
||||||
|
onSelected = { timeWindow.value = TimeWindow.DAY },
|
||||||
|
text = stringResource(id = R.string.time_window_day),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = timeWindow.value == TimeWindow.WEEK,
|
||||||
|
onSelected = { timeWindow.value = TimeWindow.WEEK },
|
||||||
|
text = stringResource(id = R.string.time_window_week),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PagingPosterGrid(
|
||||||
|
lazyPagingItems = mediaListItems,
|
||||||
|
onClick = { id ->
|
||||||
|
appNavController.navigate(
|
||||||
|
AppNavItem.DetailView.withArgs(mediaType, id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaTabs(
|
fun MediaTabs(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@@ -6,6 +6,7 @@ import com.owenlejeune.tvtime.R
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
|
||||||
import com.owenlejeune.tvtime.ui.screens.tabs.MediaTabContent
|
import com.owenlejeune.tvtime.ui.screens.tabs.MediaTabContent
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.tabs.MediaTabTrendingContent
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
@@ -27,12 +28,13 @@ sealed class MediaTabNavItem(
|
|||||||
POPULAR,
|
POPULAR,
|
||||||
NOW_PLAYING,
|
NOW_PLAYING,
|
||||||
UPCOMING,
|
UPCOMING,
|
||||||
TOP_RATED
|
TOP_RATED,
|
||||||
|
TRENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MovieItems = listOf(NowPlaying, Popular, Upcoming, TopRated)
|
private val MovieItems = listOf(NowPlaying, Popular, Trending, Upcoming, TopRated)
|
||||||
val TvItems = listOf(OnTheAir, Popular, AiringToday, TopRated)
|
private val TvItems = listOf(OnTheAir, Popular, Trending, AiringToday, TopRated)
|
||||||
|
|
||||||
fun itemsForType(type: MediaViewType): List<MediaTabNavItem> {
|
fun itemsForType(type: MediaViewType): List<MediaTabNavItem> {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
@@ -41,12 +43,6 @@ sealed class MediaTabNavItem(
|
|||||||
else -> throw ViewableMediaTypeException(type)
|
else -> throw ViewableMediaTypeException(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val Items = listOf(NowPlaying, Popular, TopRated, Upcoming, AiringToday, OnTheAir)
|
|
||||||
|
|
||||||
fun getByRoute(route: String?): MediaTabNavItem? {
|
|
||||||
return Items.firstOrNull { it.route == route }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Popular: MediaTabNavItem(
|
object Popular: MediaTabNavItem(
|
||||||
@@ -85,12 +81,23 @@ sealed class MediaTabNavItem(
|
|||||||
screen = screenContent,
|
screen = screenContent,
|
||||||
type = Type.UPCOMING
|
type = Type.UPCOMING
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object Trending: MediaTabNavItem(
|
||||||
|
stringRes = R.string.nav_trending_title,
|
||||||
|
route = "trending_route",
|
||||||
|
screen = trendingScreenContent,
|
||||||
|
type = Type.TRENDING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val screenContent: MediaNavComposableFun = { appNavController, mediaViewType, mediaTabItem ->
|
private val screenContent: MediaNavComposableFun = { appNavController, mediaViewType, mediaTabItem ->
|
||||||
MediaTabContent(appNavController = appNavController, mediaType = mediaViewType, mediaTabItem = mediaTabItem)
|
MediaTabContent(appNavController = appNavController, mediaType = mediaViewType, mediaTabItem = mediaTabItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val trendingScreenContent: MediaNavComposableFun = { appNavController, mediaViewType, mediaTabItem ->
|
||||||
|
MediaTabTrendingContent(appNavController = appNavController, mediaType = mediaViewType, mediaTabItem = mediaTabItem)
|
||||||
|
}
|
||||||
|
|
||||||
typealias MediaNavComposableFun = @Composable (NavHostController, MediaViewType, MediaTabNavItem) -> Unit
|
typealias MediaNavComposableFun = @Composable (NavHostController, MediaViewType, MediaTabNavItem) -> Unit
|
||||||
|
|
||||||
typealias MediaFetchFun = suspend (service: HomePageService, page: Int) -> Response<out HomePageResponse>
|
typealias MediaFetchFun = suspend (service: HomePageService, page: Int) -> Response<out HomePageResponse>
|
||||||
@@ -1,19 +1,38 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.tabs
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
|
import com.google.accompanist.pager.PagerState
|
||||||
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
|
||||||
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.SearchView
|
import com.owenlejeune.tvtime.ui.components.SearchView
|
||||||
|
import com.owenlejeune.tvtime.ui.components.SelectableTextChip
|
||||||
|
import com.owenlejeune.tvtime.ui.components.Tabs
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.HomeScreenViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.HomeScreenViewModel
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PeopleTab(
|
fun PeopleTab(
|
||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
@@ -28,17 +47,69 @@ fun PeopleTab(
|
|||||||
mediaType = MediaViewType.PERSON
|
mediaType = MediaViewType.PERSON
|
||||||
)
|
)
|
||||||
|
|
||||||
val mainViewModel = viewModel<MainViewModel>()
|
val tabs = PeopleTabNavItem.Items
|
||||||
val peopleList = mainViewModel.popularPeople.collectAsLazyPagingItems()
|
val pagerState = rememberPagerState()
|
||||||
|
Tabs(tabs = tabs, pagerState = pagerState)
|
||||||
|
PeopleTabs(
|
||||||
|
tabs = tabs,
|
||||||
|
pagerState = pagerState,
|
||||||
|
appNavController = appNavController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PagingPeoplePosterGrid(
|
@Composable
|
||||||
lazyPagingItems = peopleList,
|
fun PopularPeopleContent(
|
||||||
header = {
|
appNavController: NavController
|
||||||
// Text(
|
) {
|
||||||
// text = stringResource(R.string.popular_today_header),
|
val mainViewModel = viewModel<MainViewModel>()
|
||||||
// modifier = Modifier.padding(start = 8.dp)
|
val peopleList = mainViewModel.popularPeople.collectAsLazyPagingItems()
|
||||||
// )
|
|
||||||
},
|
PagingPeoplePosterGrid(
|
||||||
|
lazyPagingItems = peopleList,
|
||||||
|
onClick = { id ->
|
||||||
|
appNavController.navigate(
|
||||||
|
AppNavItem.DetailView.withArgs(MediaViewType.PERSON, id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TrendingPeopleContent(
|
||||||
|
appNavController: NavController
|
||||||
|
) {
|
||||||
|
val viewModel = viewModel<MainViewModel>()
|
||||||
|
|
||||||
|
val timeWindow = remember { mutableStateOf(TimeWindow.DAY) }
|
||||||
|
val flow = remember { mutableStateOf(viewModel.produceTrendingFor(MediaViewType.PERSON, timeWindow.value)) }
|
||||||
|
|
||||||
|
LaunchedEffect(timeWindow.value) {
|
||||||
|
flow.value = viewModel.produceTrendingFor(MediaViewType.PERSON, timeWindow.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mediaListItems = flow.value.collectAsLazyPagingItems()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
modifier = Modifier.padding(all = 12.dp)
|
||||||
|
) {
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = timeWindow.value == TimeWindow.DAY,
|
||||||
|
onSelected = { timeWindow.value = TimeWindow.DAY },
|
||||||
|
text = stringResource(id = R.string.time_window_day),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = timeWindow.value == TimeWindow.WEEK,
|
||||||
|
onSelected = { timeWindow.value = TimeWindow.WEEK },
|
||||||
|
text = stringResource(id = R.string.time_window_week),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PagingPosterGrid(
|
||||||
|
lazyPagingItems = mediaListItems,
|
||||||
onClick = { id ->
|
onClick = { id ->
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
AppNavItem.DetailView.withArgs(MediaViewType.PERSON, id)
|
AppNavItem.DetailView.withArgs(MediaViewType.PERSON, id)
|
||||||
@@ -46,4 +117,16 @@ fun PeopleTab(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun PeopleTabs(
|
||||||
|
tabs: List<PeopleTabNavItem>,
|
||||||
|
pagerState: PagerState,
|
||||||
|
appNavController: NavHostController = rememberNavController()
|
||||||
|
) {
|
||||||
|
HorizontalPager(count = tabs.size, state = pagerState) { page ->
|
||||||
|
tabs[page].screen(appNavController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
sealed class PeopleTabNavItem(
|
||||||
|
stringRes: Int,
|
||||||
|
route: String,
|
||||||
|
val screen: @Composable (NavController) -> Unit
|
||||||
|
): TabNavItem(route) {
|
||||||
|
private val resourceUtils: ResourceUtils by inject()
|
||||||
|
|
||||||
|
override val name: String = resourceUtils.getString(stringRes)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Items by lazy { listOf(Popular, Trending) }
|
||||||
|
}
|
||||||
|
|
||||||
|
object Popular: PeopleTabNavItem(
|
||||||
|
stringRes = R.string.popular_today_header,
|
||||||
|
route = "people_popular_route",
|
||||||
|
screen = { PopularPeopleContent(appNavController = it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
object Trending: PeopleTabNavItem(
|
||||||
|
stringRes = R.string.nav_trending_title,
|
||||||
|
route = "people_trending_route",
|
||||||
|
screen = { TrendingPeopleContent(appNavController = it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.ListV4Service
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.DeleteListItemsBody
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.DeleteListItemsBody
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.DeleteListItemsItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.DeleteListItemsItem
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListUpdateBody
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListUpdateBody
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
import com.owenlejeune.tvtime.ui.screens.tabs.AccountTabNavItem
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.utils.types.ViewableMediaTypeException
|
import com.owenlejeune.tvtime.utils.types.ViewableMediaTypeException
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultMedia
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Video
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Video
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
import com.owenlejeune.tvtime.ui.screens.tabs.MediaTabNavItem
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TimeWindow
|
||||||
import com.owenlejeune.tvtime.utils.types.ViewableMediaTypeException
|
import com.owenlejeune.tvtime.utils.types.ViewableMediaTypeException
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
@@ -46,22 +47,30 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
val movieAccountStates = movieService.accountStates
|
val movieAccountStates = movieService.accountStates
|
||||||
val movieKeywordResults = movieService.keywordResults
|
val movieKeywordResults = movieService.keywordResults
|
||||||
|
|
||||||
val popularMovies = createPagingFlow(
|
val popularMovies by lazy {
|
||||||
fetcher = { p -> movieService.getPopular(p) },
|
createPagingFlow(
|
||||||
processor = { it.results }
|
fetcher = { p -> movieService.getPopular(p) },
|
||||||
)
|
processor = { it.results }
|
||||||
val topRatedMovies = createPagingFlow(
|
)
|
||||||
fetcher = { p -> movieService.getTopRated(p) },
|
}
|
||||||
processor = { it.results }
|
val topRatedMovies by lazy {
|
||||||
)
|
createPagingFlow(
|
||||||
val nowPlayingMovies = createPagingFlow(
|
fetcher = { p -> movieService.getTopRated(p) },
|
||||||
fetcher = { p -> movieService.getNowPlaying(p) },
|
processor = { it.results }
|
||||||
processor = { it.results }
|
)
|
||||||
)
|
}
|
||||||
val upcomingMovies = createPagingFlow(
|
val nowPlayingMovies by lazy {
|
||||||
fetcher = { p -> movieService.getUpcoming(p) },
|
createPagingFlow(
|
||||||
processor = { it.results }
|
fetcher = { p -> movieService.getNowPlaying(p) },
|
||||||
)
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val upcomingMovies by lazy {
|
||||||
|
createPagingFlow(
|
||||||
|
fetcher = { p -> movieService.getUpcoming(p) },
|
||||||
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val detailedTv = tvService.detailTv
|
val detailedTv = tvService.detailTv
|
||||||
val tvImages = tvService.images
|
val tvImages = tvService.images
|
||||||
@@ -78,22 +87,30 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
val tvAccountStates = tvService.accountStates
|
val tvAccountStates = tvService.accountStates
|
||||||
val tvKeywordResults = tvService.keywordResults
|
val tvKeywordResults = tvService.keywordResults
|
||||||
|
|
||||||
val popularTv = createPagingFlow(
|
val popularTv by lazy {
|
||||||
fetcher = { p -> tvService.getPopular(p) },
|
createPagingFlow(
|
||||||
processor = { it.results }
|
fetcher = { p -> tvService.getPopular(p) },
|
||||||
)
|
processor = { it.results }
|
||||||
val topRatedTv = createPagingFlow(
|
)
|
||||||
fetcher = { p -> tvService.getTopRated(p) },
|
}
|
||||||
processor = { it.results }
|
val topRatedTv by lazy{
|
||||||
)
|
createPagingFlow(
|
||||||
val airingTodayTv = createPagingFlow(
|
fetcher = { p -> tvService.getTopRated(p) },
|
||||||
fetcher = { p -> tvService.getNowPlaying(p) },
|
processor = { it.results }
|
||||||
processor = { it.results }
|
)
|
||||||
)
|
}
|
||||||
val onTheAirTv = createPagingFlow(
|
val airingTodayTv by lazy {
|
||||||
fetcher = { p -> tvService.getUpcoming(p) },
|
createPagingFlow(
|
||||||
processor = { it.results }
|
fetcher = { p -> tvService.getNowPlaying(p) },
|
||||||
)
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val onTheAirTv by lazy {
|
||||||
|
createPagingFlow(
|
||||||
|
fetcher = { p -> tvService.getUpcoming(p) },
|
||||||
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val peopleMap = peopleService.peopleMap
|
val peopleMap = peopleService.peopleMap
|
||||||
val peopleCastMap = peopleService.castMap
|
val peopleCastMap = peopleService.castMap
|
||||||
@@ -101,10 +118,12 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
val peopleImagesMap = peopleService.imagesMap
|
val peopleImagesMap = peopleService.imagesMap
|
||||||
val peopleExternalIdsMap = peopleService.externalIdsMap
|
val peopleExternalIdsMap = peopleService.externalIdsMap
|
||||||
|
|
||||||
val popularPeople = createPagingFlow(
|
val popularPeople by lazy {
|
||||||
fetcher = { p -> peopleService.getPopular(p) },
|
createPagingFlow(
|
||||||
processor = { it.results }
|
fetcher = { p -> peopleService.getPopular(p) },
|
||||||
)
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T> providesForType(type: MediaViewType, movies: () -> T, tv: () -> T): T {
|
private fun <T> providesForType(type: MediaViewType, movies: () -> T, tv: () -> T): T {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
@@ -167,6 +186,7 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
MediaTabNavItem.Type.TOP_RATED -> topRatedMovies
|
MediaTabNavItem.Type.TOP_RATED -> topRatedMovies
|
||||||
MediaTabNavItem.Type.NOW_PLAYING -> nowPlayingMovies
|
MediaTabNavItem.Type.NOW_PLAYING -> nowPlayingMovies
|
||||||
MediaTabNavItem.Type.POPULAR -> popularMovies
|
MediaTabNavItem.Type.POPULAR -> popularMovies
|
||||||
|
else -> throw Exception("Can't produce media flow for $mediaType")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -175,6 +195,7 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
MediaTabNavItem.Type.TOP_RATED -> topRatedTv
|
MediaTabNavItem.Type.TOP_RATED -> topRatedTv
|
||||||
MediaTabNavItem.Type.NOW_PLAYING -> airingTodayTv
|
MediaTabNavItem.Type.NOW_PLAYING -> airingTodayTv
|
||||||
MediaTabNavItem.Type.POPULAR -> popularTv
|
MediaTabNavItem.Type.POPULAR -> popularTv
|
||||||
|
else -> throw Exception("Can't produce media flow for $mediaType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -184,6 +205,30 @@ class MainViewModel: ViewModel(), KoinComponent {
|
|||||||
return providesForType(mediaType, { movieKeywordResults }, { tvKeywordResults })
|
return providesForType(mediaType, { movieKeywordResults }, { tvKeywordResults })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun produceTrendingFor(mediaType: MediaViewType, timeWindow: TimeWindow): Flow<PagingData<TmdbItem>> {
|
||||||
|
return when (mediaType) {
|
||||||
|
MediaViewType.MOVIE -> {
|
||||||
|
createPagingFlow(
|
||||||
|
fetcher = { p -> movieService.getTrending(timeWindow, p) },
|
||||||
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MediaViewType.TV -> {
|
||||||
|
createPagingFlow(
|
||||||
|
fetcher = { p -> tvService.getTrending(timeWindow, p) },
|
||||||
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MediaViewType.PERSON -> {
|
||||||
|
createPagingFlow(
|
||||||
|
fetcher = { p -> peopleService.getTrending(timeWindow, p) },
|
||||||
|
processor = { it.results }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> throw ViewableMediaTypeException(mediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getById(id: Int, type: MediaViewType) {
|
suspend fun getById(id: Int, type: MediaViewType) {
|
||||||
when (type) {
|
when (type) {
|
||||||
MediaViewType.MOVIE -> movieService.getById(id)
|
MediaViewType.MOVIE -> movieService.getById(id)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.owenlejeune.tvtime.utils.types
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
enum class TimeWindow {
|
||||||
|
@SerializedName("day")
|
||||||
|
DAY,
|
||||||
|
@SerializedName("week")
|
||||||
|
WEEK
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<string name="nav_upcoming_title">Upcoming</string>
|
<string name="nav_upcoming_title">Upcoming</string>
|
||||||
<string name="nav_tv_airing_today_title">Airing Today</string>
|
<string name="nav_tv_airing_today_title">Airing Today</string>
|
||||||
<string name="nav_tv_on_the_air">On The Air</string>
|
<string name="nav_tv_on_the_air">On The Air</string>
|
||||||
|
<string name="nav_trending_title">Trending</string>
|
||||||
<string name="nav_account_title">Account</string>
|
<string name="nav_account_title">Account</string>
|
||||||
<string name="nav_rated_movies_title">Rated Movies</string>
|
<string name="nav_rated_movies_title">Rated Movies</string>
|
||||||
<string name="nav_rated_shows_title">Rated TV Shows</string>
|
<string name="nav_rated_shows_title">Rated TV Shows</string>
|
||||||
@@ -216,6 +217,9 @@
|
|||||||
<string name="recommended_tv_title">Recommended TV</string>
|
<string name="recommended_tv_title">Recommended TV</string>
|
||||||
<string name="no_recommended_tv">No Recommended TV</string>
|
<string name="no_recommended_tv">No Recommended TV</string>
|
||||||
<string name="no_result_found">No more results found</string>
|
<string name="no_result_found">No more results found</string>
|
||||||
|
<string name="search_multi_title">Multi</string>
|
||||||
|
<string name="time_window_day">Day</string>
|
||||||
|
<string name="time_window_week">Week</string>
|
||||||
|
|
||||||
<string name="attribution_text">This product uses the TMDB API but is not endorsed or certified by TMDB.</string>
|
<string name="attribution_text">This product uses the TMDB API but is not endorsed or certified by TMDB.</string>
|
||||||
<string name="tmdb_home_page">"https://www.themoviedb.org"</string>
|
<string name="tmdb_home_page">"https://www.themoviedb.org"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user