mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-08 04:32:43 -05:00
add screen for movies/tv by keyword from details page
This commit is contained in:
@@ -29,4 +29,6 @@ interface DetailService {
|
||||
|
||||
suspend fun getAccountStates(id: Int)
|
||||
|
||||
suspend fun discover(keywords: String? = null, page: Int): Response<out SearchResult<out SearchResultMedia>>
|
||||
|
||||
}
|
||||
@@ -64,4 +64,7 @@ interface MoviesApi {
|
||||
@GET("movie/{id}/account_states")
|
||||
suspend fun getAccountStates(@Path("id") id: Int, @Query("session_id") sessionId: String): Response<AccountStates>
|
||||
|
||||
@GET("discover/movie")
|
||||
suspend fun discover(@Query("with_keywords") keywords: String? = null, @Query("page") page: Int): Response<SearchResult<SearchResultMovie>>
|
||||
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.MovieReleaseResults
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Review
|
||||
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.SearchResultMovie
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Searchable
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SortableSearchResult
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
||||
@@ -53,6 +55,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
||||
val releaseDates = Collections.synchronizedMap(mutableStateMapOf<Int, List<MovieReleaseResults.ReleaseDateResult>>())
|
||||
val similar = Collections.synchronizedMap(mutableStateMapOf<Int, Flow<PagingData<TmdbItem>>>())
|
||||
val accountStates = Collections.synchronizedMap(mutableStateMapOf<Int, AccountStates>())
|
||||
val keywordResults = Collections.synchronizedMap(mutableStateMapOf<Int, Flow<PagingData<SearchResultMedia>>>())
|
||||
|
||||
|
||||
override suspend fun getById(id: Int) {
|
||||
@@ -134,6 +137,9 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||
return movieService.discover(keywords, page)
|
||||
}
|
||||
|
||||
override suspend fun getSimilar(id: Int, page: Int): Response<out HomePageResponse> {
|
||||
return movieService.getSimilarMovies(id, page)
|
||||
|
||||
@@ -67,4 +67,6 @@ interface TvApi {
|
||||
@GET("tv/{id}/account_states")
|
||||
suspend fun getAccountStates(@Path("id") id: Int): Response<AccountStates>
|
||||
|
||||
@GET("discover/tv")
|
||||
suspend fun discover(@Query("page") page: Int, @Query("with_keywords") keywords: String? = null): Response<SearchResult<SearchResultTv>>
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KeywordsResponse
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Review
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ReviewResponse
|
||||
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.Season
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||
@@ -57,6 +59,7 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
||||
val contentRatings = Collections.synchronizedMap(mutableStateMapOf<Int, List<TvContentRatings.TvContentRating>>())
|
||||
val similar = Collections.synchronizedMap(mutableStateMapOf<Int, Flow<PagingData<TmdbItem>>>())
|
||||
val accountStates = Collections.synchronizedMap(mutableStateMapOf<Int, AccountStates>())
|
||||
val keywordResults = Collections.synchronizedMap(mutableStateMapOf<Int, Flow<PagingData<SearchResultMedia>>>())
|
||||
|
||||
private val _seasons = Collections.synchronizedMap(mutableStateMapOf<Int, MutableSet<Season>>())
|
||||
val seasons: MutableMap<Int, out Set<Season>>
|
||||
@@ -137,6 +140,10 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun discover(keywords: String?, page: Int): Response<out SearchResult<out SearchResultMedia>> {
|
||||
return service.discover(page, keywords)
|
||||
}
|
||||
|
||||
override suspend fun getSimilar(id: Int, page: Int): Response<out HomePageResponse> {
|
||||
return service.getSimilarTvShows(id, page)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.owenlejeune.tvtime.extensions
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun <T: Serializable> Bundle.safeGetSerializable(key: String, clazz: Class<T>): T? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getSerializable(key, clazz)
|
||||
} else {
|
||||
getSerializable(key) as? T
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ fun ActionButton(
|
||||
.clip(CircleShape)
|
||||
.height(40.dp)
|
||||
.requiredWidthIn(min = 40.dp)
|
||||
.background(color = MaterialTheme.colorScheme.actionButtonColor)
|
||||
.background(color = MaterialTheme.colorScheme.tertiary)
|
||||
.clickable(onClick = onClick)
|
||||
) {
|
||||
Icon(
|
||||
|
||||
@@ -222,6 +222,8 @@ fun SearchView(
|
||||
val homeScreenViewModel = viewModel<HomeScreenViewModel>()
|
||||
homeScreenViewModel.fab.value = @Composable {
|
||||
FloatingActionButton(
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.tertiary,
|
||||
onClick = {
|
||||
appNavController.navigate(route)
|
||||
}
|
||||
@@ -279,7 +281,8 @@ fun MinLinesText(
|
||||
|
||||
class ChipInfo(
|
||||
val text: String,
|
||||
val enabled: Boolean = true
|
||||
val enabled: Boolean = true,
|
||||
val id: Int? = null
|
||||
)
|
||||
|
||||
sealed class ChipStyle(val mainAxisSpacing: Dp, val crossAxisSpacing: Dp) {
|
||||
@@ -329,11 +332,10 @@ object ChipDefaults {
|
||||
|
||||
@Composable
|
||||
fun BoxyChip(
|
||||
text: String,
|
||||
chipInfo: ChipInfo,
|
||||
style: TextStyle = MaterialTheme.typography.bodySmall,
|
||||
isSelected: Boolean = true,
|
||||
onSelectionChanged: (String) -> Unit = {},
|
||||
enabled: Boolean = true,
|
||||
onSelectionChanged: (ChipInfo) -> Unit = {},
|
||||
colors: ChipColors = ChipDefaults.boxyChipColors()
|
||||
) {
|
||||
Surface(
|
||||
@@ -346,13 +348,13 @@ fun BoxyChip(
|
||||
.toggleable(
|
||||
value = isSelected,
|
||||
onValueChange = {
|
||||
onSelectionChanged(text)
|
||||
onSelectionChanged(chipInfo)
|
||||
},
|
||||
enabled = enabled
|
||||
enabled = chipInfo.enabled
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
text = chipInfo.text,
|
||||
style = style,
|
||||
color = if (isSelected) colors.selectedContentColor() else colors.unselectedContentColor(),
|
||||
modifier = Modifier.padding(8.dp)
|
||||
@@ -363,11 +365,10 @@ fun BoxyChip(
|
||||
|
||||
@Composable
|
||||
fun RoundedChip(
|
||||
text: String,
|
||||
chipInfo: ChipInfo,
|
||||
style: TextStyle = MaterialTheme.typography.bodySmall,
|
||||
isSelected: Boolean = false,
|
||||
onSelectionChanged: (String) -> Unit = {},
|
||||
enabled: Boolean = true,
|
||||
onSelectionChanged: (ChipInfo) -> Unit = {},
|
||||
colors: ChipColors = ChipDefaults.roundedChipColors()
|
||||
) {
|
||||
val borderColor = if (isSelected) colors.selectedContainerColor() else colors.unselectedContainerColor()
|
||||
@@ -382,14 +383,14 @@ fun RoundedChip(
|
||||
.toggleable(
|
||||
value = isSelected,
|
||||
onValueChange = {
|
||||
onSelectionChanged(text)
|
||||
onSelectionChanged(chipInfo)
|
||||
},
|
||||
enabled = enabled
|
||||
enabled = chipInfo.enabled
|
||||
)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
text = chipInfo.text,
|
||||
style = style,
|
||||
color = if (isSelected) colors.selectedContentColor() else colors.unselectedContentColor()
|
||||
)
|
||||
@@ -401,7 +402,7 @@ fun RoundedChip(
|
||||
fun ChipGroup(
|
||||
modifier: Modifier = Modifier,
|
||||
chips: List<ChipInfo> = emptyList(),
|
||||
onSelectedChanged: (String) -> Unit = {},
|
||||
onSelectedChanged: (ChipInfo) -> Unit = {},
|
||||
chipStyle: ChipStyle = ChipStyle.Boxy,
|
||||
roundedChipColors: ChipColors = ChipDefaults.roundedChipColors(),
|
||||
boxyChipColors: ChipColors = ChipDefaults.boxyChipColors()
|
||||
@@ -412,17 +413,15 @@ fun ChipGroup(
|
||||
when (chipStyle) {
|
||||
ChipStyle.Boxy -> {
|
||||
BoxyChip(
|
||||
text = chip.text,
|
||||
chipInfo = chip,
|
||||
onSelectionChanged = onSelectedChanged,
|
||||
enabled = chip.enabled,
|
||||
colors = boxyChipColors
|
||||
)
|
||||
}
|
||||
ChipStyle.Rounded -> {
|
||||
RoundedChip(
|
||||
text = chip.text,
|
||||
chipInfo = chip,
|
||||
onSelectionChanged = onSelectedChanged,
|
||||
enabled = chip.enabled,
|
||||
colors = roundedChipColors
|
||||
)
|
||||
}
|
||||
@@ -453,6 +452,7 @@ fun RatingRing(
|
||||
size: Dp = 60.dp,
|
||||
ringStrokeWidth: Dp = 4.dp,
|
||||
ringColor: Color = MaterialTheme.colorScheme.primary,
|
||||
trackColor: Color = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
textColor: Color = Color.White,
|
||||
textSize: TextUnit = 14.sp
|
||||
) {
|
||||
@@ -464,7 +464,9 @@ fun RatingRing(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
progress = progress,
|
||||
strokeWidth = ringStrokeWidth,
|
||||
color = ringColor
|
||||
color = ringColor,
|
||||
trackColor = trackColor,
|
||||
strokeCap = StrokeCap.Round
|
||||
)
|
||||
|
||||
Text(
|
||||
@@ -688,7 +690,7 @@ fun UserInitials(
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(size)
|
||||
.background(color = MaterialTheme.colorScheme.secondary)
|
||||
.background(color = MaterialTheme.colorScheme.tertiary)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.owenlejeune.tvtime.ui.navigation
|
||||
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.core.tween
|
||||
@@ -15,10 +16,12 @@ import androidx.navigation.navArgument
|
||||
import androidx.navigation.navDeepLink
|
||||
import com.owenlejeune.tvtime.R
|
||||
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||
import com.owenlejeune.tvtime.extensions.safeGetSerializable
|
||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||
import com.owenlejeune.tvtime.ui.screens.AboutScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.AccountScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.HomeScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.KeywordResultsScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.ListDetailScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.MediaDetailScreen
|
||||
import com.owenlejeune.tvtime.ui.screens.PersonDetailScreen
|
||||
@@ -116,7 +119,7 @@ fun AppNavigationHost(
|
||||
)
|
||||
} else {
|
||||
Pair(
|
||||
arguments.getSerializable(NavConstants.SEARCH_ID_KEY) as MediaViewType,
|
||||
arguments.safeGetSerializable(NavConstants.SEARCH_ID_KEY, MediaViewType::class.java)!!,
|
||||
arguments.getString(NavConstants.SEARCH_TITLE_KEY) ?: ""
|
||||
)
|
||||
}
|
||||
@@ -154,6 +157,25 @@ fun AppNavigationHost(
|
||||
composable(route = AppNavItem.AboutView.route) {
|
||||
AboutScreen(appNavController = appNavController)
|
||||
}
|
||||
composable(
|
||||
route = AppNavItem.KeywordsView.route.plus("/{${NavConstants.KEYWORD_TYPE_KEY}}?keyword={${NavConstants.KEYWORD_NAME_KEY}}&keywordId={${NavConstants.KEYWORD_ID_KEY}}"),
|
||||
arguments = listOf(
|
||||
navArgument(NavConstants.KEYWORD_TYPE_KEY) { type = NavType.EnumType(MediaViewType::class.java) },
|
||||
navArgument(NavConstants.KEYWORD_NAME_KEY) { type = NavType.StringType },
|
||||
navArgument(NavConstants.KEYWORD_ID_KEY) { type = NavType.IntType }
|
||||
)
|
||||
) { navBackStackEntry ->
|
||||
val type = navBackStackEntry.arguments?.safeGetSerializable(NavConstants.KEYWORD_TYPE_KEY, MediaViewType::class.java)!!
|
||||
val keywords = navBackStackEntry.arguments?.getString(NavConstants.KEYWORD_NAME_KEY) ?: ""
|
||||
val id = navBackStackEntry.arguments?.getInt(NavConstants.KEYWORD_ID_KEY)!!
|
||||
|
||||
KeywordResultsScreen(
|
||||
type = type,
|
||||
keyword = keywords,
|
||||
id = id,
|
||||
appNavController = appNavController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,5 +200,8 @@ sealed class AppNavItem(val route: String) {
|
||||
}
|
||||
object AccountView: AppNavItem("account_route")
|
||||
object AboutView: AppNavItem("about_route")
|
||||
object KeywordsView: AppNavItem("keywords_route") {
|
||||
fun withArgs(type: MediaViewType, keyword: String, id: Int) = route.plus("/$type?keyword=$keyword&keywordId=$id")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.owenlejeune.tvtime.ui.navigation
|
||||
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Face
|
||||
import androidx.compose.material.icons.outlined.Movie
|
||||
@@ -30,7 +32,12 @@ fun HomeScreenNavHost(
|
||||
) {
|
||||
val homeScreenViewModel = viewModel<HomeScreenViewModel>()
|
||||
|
||||
NavHost(navController = navController, startDestination = startDestination) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination,
|
||||
enterTransition = { fadeIn() },
|
||||
exitTransition = { fadeOut() }
|
||||
) {
|
||||
composable(HomeScreenNavItem.Movies.route) {
|
||||
homeScreenViewModel.appBarActions.value = {}
|
||||
MediaTab(
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.owenlejeune.tvtime.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.owenlejeune.tvtime.R
|
||||
import com.owenlejeune.tvtime.extensions.lazyPagingItems
|
||||
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
|
||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun KeywordResultsScreen(
|
||||
type: MediaViewType,
|
||||
keyword: String,
|
||||
id: Int,
|
||||
appNavController: NavController
|
||||
) {
|
||||
val mainViewModel = viewModel<MainViewModel>()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
mainViewModel.getKeywordResults(id, keyword, type)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = keyword.capitalize(Locale.current)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = { appNavController.popBackStack() }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Box(modifier = Modifier.padding(innerPadding)) {
|
||||
val keywordResultsMap = remember { mainViewModel.produceKeywordsResultsFor(type) }
|
||||
val keywordResults = keywordResultsMap[id]
|
||||
|
||||
val pagingResults = keywordResults?.collectAsLazyPagingItems()
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(all = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
pagingResults?.let {
|
||||
lazyPagingItems(it) { result ->
|
||||
result?.let {
|
||||
MediaResultCard(
|
||||
appNavController = appNavController,
|
||||
mediaViewType = type,
|
||||
id = result.id,
|
||||
backdropPath = TmdbUtils.getFullBackdropPath(result.backdropPath),
|
||||
posterPath = TmdbUtils.getFullPosterPath(result.posterPath),
|
||||
title = result.name,
|
||||
additionalDetails = listOf(result.overview)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,7 +385,7 @@ private fun MainContent(
|
||||
windowSize: WindowSizeClass,
|
||||
mainViewModel: MainViewModel
|
||||
) {
|
||||
OverviewCard(itemId = itemId, mediaItem = mediaItem, type = type, mainViewModel = mainViewModel)
|
||||
OverviewCard(itemId = itemId, mediaItem = mediaItem, type = type, mainViewModel = mainViewModel, appNavController = appNavController)
|
||||
|
||||
CastCard(itemId = itemId, appNavController = appNavController, type = type, mainViewModel = mainViewModel)
|
||||
|
||||
@@ -500,11 +500,12 @@ private fun MiscDetails(
|
||||
|
||||
@Composable
|
||||
private fun OverviewCard(
|
||||
modifier: Modifier = Modifier,
|
||||
itemId: Int,
|
||||
mediaItem: DetailedItem?,
|
||||
type: MediaViewType,
|
||||
mainViewModel: MainViewModel
|
||||
mainViewModel: MainViewModel,
|
||||
appNavController: NavController,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
mainViewModel.getKeywords(itemId, type)
|
||||
@@ -530,7 +531,8 @@ private fun OverviewCard(
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = tagline,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = MaterialTheme.colorScheme.tertiary
|
||||
,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
@@ -545,7 +547,7 @@ private fun OverviewCard(
|
||||
|
||||
|
||||
keywords?.let { keywords ->
|
||||
val keywordsChipInfo = keywords.map { ChipInfo(it.name, false) }
|
||||
val keywordsChipInfo = keywords.map { ChipInfo(it.name, true, it.id) }
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(ChipStyle.Rounded.mainAxisSpacing)
|
||||
@@ -553,16 +555,13 @@ private fun OverviewCard(
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
keywordsChipInfo.forEach { keywordChipInfo ->
|
||||
RoundedChip(
|
||||
text = keywordChipInfo.text,
|
||||
enabled = keywordChipInfo.enabled,
|
||||
chipInfo = keywordChipInfo,
|
||||
colors = ChipDefaults.roundedChipColors(
|
||||
unselectedContainerColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedContentColor = MaterialTheme.colorScheme.primary
|
||||
),
|
||||
onSelectionChanged = { chip ->
|
||||
// if (service is MoviesService) {
|
||||
// // Toast.makeText(context, chip, Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
appNavController.navigate(AppNavItem.KeywordsView.withArgs(type, chip.text, chip.id!!))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ fun MediaTabContent(
|
||||
mediaTabItem: MediaTabNavItem
|
||||
) {
|
||||
val viewModel = viewModel<MainViewModel>()
|
||||
val mediaListItems = viewModel.produceFlowFor(mediaType, mediaTabItem.type).collectAsLazyPagingItems()
|
||||
val mediaListItems = viewModel.produceMediaTabFlowFor(mediaType, mediaTabItem.type).collectAsLazyPagingItems()
|
||||
|
||||
PagingPosterGrid(
|
||||
lazyPagingItems = mediaListItems,
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Keyword
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Review
|
||||
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.Video
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviders
|
||||
@@ -43,6 +44,7 @@ class MainViewModel: ViewModel(), KoinComponent {
|
||||
val movieReleaseDates = movieService.releaseDates
|
||||
val similarMovies = movieService.similar
|
||||
val movieAccountStates = movieService.accountStates
|
||||
val movieKeywordResults = movieService.keywordResults
|
||||
|
||||
val popularMovies = createPagingFlow(
|
||||
fetcher = { p -> movieService.getPopular(p) },
|
||||
@@ -74,6 +76,7 @@ class MainViewModel: ViewModel(), KoinComponent {
|
||||
val tvSeasons = tvService.seasons
|
||||
val similarTv = tvService.similar
|
||||
val tvAccountStates = tvService.accountStates
|
||||
val tvKeywordResults = tvService.keywordResults
|
||||
|
||||
val popularTv = createPagingFlow(
|
||||
fetcher = { p -> tvService.getPopular(p) },
|
||||
@@ -155,7 +158,7 @@ class MainViewModel: ViewModel(), KoinComponent {
|
||||
return providesForType(type, { movieAccountStates }, { tvAccountStates} )
|
||||
}
|
||||
|
||||
fun produceFlowFor(mediaType: MediaViewType, contentType: MediaTabNavItem.Type): Flow<PagingData<TmdbItem>> {
|
||||
fun produceMediaTabFlowFor(mediaType: MediaViewType, contentType: MediaTabNavItem.Type): Flow<PagingData<TmdbItem>> {
|
||||
return providesForType(
|
||||
mediaType,
|
||||
{
|
||||
@@ -177,6 +180,10 @@ class MainViewModel: ViewModel(), KoinComponent {
|
||||
)
|
||||
}
|
||||
|
||||
fun produceKeywordsResultsFor(mediaType: MediaViewType): Map<Int, Flow<PagingData<SearchResultMedia>>> {
|
||||
return providesForType(mediaType, { movieKeywordResults }, { tvKeywordResults })
|
||||
}
|
||||
|
||||
suspend fun getById(id: Int, type: MediaViewType) {
|
||||
when (type) {
|
||||
MediaViewType.MOVIE -> movieService.getById(id)
|
||||
@@ -287,6 +294,24 @@ class MainViewModel: ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
fun getKeywordResults(id: Int, keyword: String, type: MediaViewType) {
|
||||
when (type) {
|
||||
MediaViewType.MOVIE -> {
|
||||
movieKeywordResults[id] = createPagingFlow(
|
||||
fetcher = { p -> movieService.discover(keyword, p) },
|
||||
processor = { it.results }
|
||||
)
|
||||
}
|
||||
MediaViewType.TV -> {
|
||||
tvKeywordResults[id] = createPagingFlow(
|
||||
fetcher = { p -> tvService.discover(keyword, p) },
|
||||
processor = { it.results }
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReleaseDates(id: Int) {
|
||||
movieService.getReleaseDates(id)
|
||||
}
|
||||
|
||||
@@ -9,4 +9,7 @@ object NavConstants {
|
||||
const val ACCOUNT_KEY = "account_key"
|
||||
const val AUTH_REDIRECT_PAGE = "return"
|
||||
const val WEB_LINK_KEY = "web_link_key"
|
||||
const val KEYWORD_TYPE_KEY = "keyword_type_key"
|
||||
const val KEYWORD_NAME_KEY = "keyword_name_key"
|
||||
const val KEYWORD_ID_KEY = "keyword_id_key"
|
||||
}
|
||||
Reference in New Issue
Block a user