mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-08 12:42:44 -05:00
refactor main view titles and poster placeholders
This commit is contained in:
@@ -92,13 +92,13 @@ class MainActivity : MonetCompatActivity() {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
val appBarTitle = rememberSaveable { mutableStateOf(BottomNavItem.getByRoute(currentRoute)?.name ?: BottomNavItem.SortedItems[0].name) }
|
||||
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val scrollBehavior = remember(decayAnimationSpec) {
|
||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
|
||||
}
|
||||
|
||||
val appBarTitle = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
||||
val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) }
|
||||
val fab = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
||||
|
||||
@@ -108,7 +108,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
if (windowSize != WindowSizeClass.Expanded) {
|
||||
TopBar(
|
||||
appNavController = appNavController,
|
||||
title = appBarTitle,
|
||||
title = appBarTitle.value,
|
||||
scrollBehavior = scrollBehavior,
|
||||
appBarActions = appBarActions
|
||||
)
|
||||
@@ -119,7 +119,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
},
|
||||
bottomBar = {
|
||||
if (windowSize != WindowSizeClass.Expanded) {
|
||||
BottomNavBar(navController = navController, appBarTitle = appBarTitle)
|
||||
BottomNavBar(navController = navController)
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
@@ -141,7 +141,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
appNavController: NavHostController,
|
||||
title: MutableState<String>,
|
||||
title: @Composable () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({})
|
||||
) {
|
||||
@@ -155,7 +155,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
}
|
||||
}
|
||||
LargeTopAppBar(
|
||||
title = { Text(text = title.value) },
|
||||
title = title,
|
||||
scrollBehavior = scrollBehavior,
|
||||
colors = TopAppBarDefaults
|
||||
.largeTopAppBarColors(
|
||||
@@ -171,7 +171,6 @@ class MainActivity : MonetCompatActivity() {
|
||||
@Composable
|
||||
private fun BottomNavBar(
|
||||
navController: NavController,
|
||||
appBarTitle: MutableState<String>,
|
||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||
) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
@@ -192,11 +191,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (!isSelected) {
|
||||
onBottomAppBarItemClicked(
|
||||
navController = navController,
|
||||
appBarTitle = appBarTitle,
|
||||
item = item
|
||||
)
|
||||
navController.navigateInBottomBar(item.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -204,15 +199,6 @@ class MainActivity : MonetCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBottomAppBarItemClicked(
|
||||
navController: NavController,
|
||||
appBarTitle: MutableState<String>,
|
||||
item: BottomNavItem
|
||||
) {
|
||||
navController.navigateInBottomBar(item.route)
|
||||
appBarTitle.value = item.name
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainContent(
|
||||
windowSize: WindowSizeClass,
|
||||
@@ -220,7 +206,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
navController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>,
|
||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
||||
) {
|
||||
@@ -251,7 +237,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
appNavController: NavHostController,
|
||||
navController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
||||
) {
|
||||
@@ -271,7 +257,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
navController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>,
|
||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route,
|
||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||
@@ -290,11 +276,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (!isSelected) {
|
||||
onBottomAppBarItemClicked(
|
||||
navController = navController,
|
||||
appBarTitle = appBarTitle,
|
||||
item = item
|
||||
)
|
||||
navController.navigateInBottomBar(item.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -307,7 +289,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
Column {
|
||||
TopBar(
|
||||
appNavController = appNavController,
|
||||
title = appBarTitle,
|
||||
title = appBarTitle.value,
|
||||
scrollBehavior = topBarScrollBehaviour,
|
||||
appBarActions = appBarActions
|
||||
)
|
||||
@@ -328,7 +310,7 @@ class MainActivity : MonetCompatActivity() {
|
||||
appNavController: NavHostController,
|
||||
navController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<RowScope.() -> Unit> = mutableStateOf({}),
|
||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
||||
) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.owenlejeune.tvtime.R
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
||||
import com.owenlejeune.tvtime.ui.navigation.MediaFetchFun
|
||||
import org.koin.core.Koin
|
||||
@@ -13,9 +15,14 @@ import retrofit2.Response
|
||||
|
||||
class HomePagePagingSource(
|
||||
private val service: HomePageService,
|
||||
private val mediaFetch: MediaFetchFun
|
||||
private val mediaFetch: MediaFetchFun,
|
||||
private val tag: String
|
||||
): PagingSource<Int, TmdbItem>(), KoinComponent {
|
||||
|
||||
companion object {
|
||||
val TAG = HomePagePagingSource::class.java.simpleName
|
||||
}
|
||||
|
||||
private val context: Context by inject()
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, TmdbItem>): Int? {
|
||||
@@ -25,17 +32,26 @@ class HomePagePagingSource(
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TmdbItem> {
|
||||
return try {
|
||||
val nextPage = params.key ?: 1
|
||||
Log.d(TAG, "Loading $tag page $nextPage")
|
||||
val mediaResponse = mediaFetch.invoke(service, nextPage)
|
||||
if (mediaResponse.isSuccessful) {
|
||||
val responseBody = mediaResponse.body()
|
||||
val results = responseBody?.results ?: emptyList()
|
||||
LoadResult.Page(
|
||||
data = results,
|
||||
prevKey = if (nextPage == 1) null else nextPage - 1,
|
||||
nextKey = if (results.isEmpty() || responseBody == null) null else responseBody.page + 1
|
||||
prevKey = if (nextPage == 1) {
|
||||
null
|
||||
} else {
|
||||
nextPage - 1
|
||||
},
|
||||
nextKey = if (results.isEmpty() || responseBody == null) {
|
||||
null
|
||||
} else {
|
||||
responseBody.page + 1
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// Toast.makeText(context, "No more results found", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, context.getString(R.string.no_result_found), Toast.LENGTH_SHORT).show()
|
||||
LoadResult.Invalid()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.owenlejeune.tvtime.ui.viewmodel
|
||||
package com.owenlejeune.tvtime.api.tmdb.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -14,18 +14,19 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||
import com.owenlejeune.tvtime.ui.navigation.MediaFetchFun
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
sealed class MediaTabViewModel(service: HomePageService, mediaFetchFun: MediaFetchFun): ViewModel() {
|
||||
val mediaItems: Flow<PagingData<TmdbItem>> = Pager(PagingConfig(pageSize = Int.MAX_VALUE)) {
|
||||
HomePagePagingSource(service = service, mediaFetch = mediaFetchFun)
|
||||
sealed class MediaTabViewModel(service: HomePageService, mediaFetchFun: MediaFetchFun, tag: String): ViewModel() {
|
||||
|
||||
val mediaItems: Flow<PagingData<TmdbItem>> = Pager(PagingConfig(pageSize = ViewModelConstants.PAGING_SIZE)) {
|
||||
HomePagePagingSource(service = service, mediaFetch = mediaFetchFun, tag = tag)
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
|
||||
object PopularMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getPopular(p) })
|
||||
object TopRatedMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getTopRated(p) })
|
||||
object NowPlayingMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getNowPlaying(p) })
|
||||
object UpcomingMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getUpcoming(p) })
|
||||
object PopularTvVM: MediaTabViewModel(TvService(), { s, p -> s.getPopular(p) })
|
||||
object TopRatedTvVM: MediaTabViewModel(TvService(), { s, p -> s.getTopRated(p) })
|
||||
object AiringTodayTvVM: MediaTabViewModel(TvService(), { s, p -> s.getNowPlaying(p) })
|
||||
object OnTheAirTvVM: MediaTabViewModel(TvService(), { s, p -> s.getUpcoming(p) })
|
||||
object PopularMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getPopular(p) }, PopularMoviesVM::class.java.simpleName)
|
||||
object TopRatedMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getTopRated(p) }, TopRatedMoviesVM::class.java.simpleName)
|
||||
object NowPlayingMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getNowPlaying(p) }, NowPlayingMoviesVM::class.java.simpleName)
|
||||
object UpcomingMoviesVM: MediaTabViewModel(MoviesService(), { s, p -> s.getUpcoming(p) }, UpcomingMoviesVM::class.java.simpleName)
|
||||
object PopularTvVM: MediaTabViewModel(TvService(), { s, p -> s.getPopular(p) }, PopularTvVM::class.java.simpleName)
|
||||
object TopRatedTvVM: MediaTabViewModel(TvService(), { s, p -> s.getTopRated(p) }, TopRatedTvVM::class.java.simpleName)
|
||||
object AiringTodayTvVM: MediaTabViewModel(TvService(), { s, p -> s.getNowPlaying(p) }, AiringTodayTvVM::class.java.simpleName)
|
||||
object OnTheAirTvVM: MediaTabViewModel(TvService(), { s, p -> s.getUpcoming(p) }, OnTheAirTvVM::class.java.simpleName)
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.owenlejeune.tvtime.ui.viewmodel
|
||||
package com.owenlejeune.tvtime.api.tmdb.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class PeopleTabViewModel: ViewModel() {
|
||||
|
||||
val popularPeople: Flow<PagingData<HomePagePerson>> = Pager(PagingConfig(pageSize = Int.MAX_VALUE)) {
|
||||
val popularPeople: Flow<PagingData<HomePagePerson>> = Pager(PagingConfig(pageSize = ViewModelConstants.PAGING_SIZE)) {
|
||||
HomePagePeoplePagingSource()
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
//package com.owenlejeune.tvtime.api.tmdb.viewmodel
|
||||
//
|
||||
//import androidx.lifecycle.ViewModel
|
||||
//import androidx.lifecycle.viewModelScope
|
||||
//import androidx.paging.Pager
|
||||
//import androidx.paging.PagingConfig
|
||||
//import androidx.paging.PagingData
|
||||
//import androidx.paging.cachedIn
|
||||
//import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PopularMovie
|
||||
//import com.owenlejeune.tvtime.api.tmdb.paging.PopularMovieSource
|
||||
//import kotlinx.coroutines.flow.Flow
|
||||
//
|
||||
//class PopularMovieViewModel: ViewModel() {
|
||||
// val moviePage: Flow<PagingData<PopularMovie>> = Pager(PagingConfig(pageSize = PopularMovieSource.MAX_PAGE)) {
|
||||
// PopularMovieSource()
|
||||
// }.flow.cachedIn(viewModelScope)
|
||||
//}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.owenlejeune.tvtime.ui.viewmodel
|
||||
package com.owenlejeune.tvtime.api.tmdb.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -8,14 +8,13 @@ import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RecommendedMediaPagingSource
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.RecommendedMedia
|
||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
sealed class RecommendedMediaViewModel(mediaType: MediaViewType): ViewModel(), KoinComponent {
|
||||
|
||||
val mediaItems: Flow<PagingData<TmdbItem>> = Pager(PagingConfig(pageSize = Int.MAX_VALUE)) {
|
||||
val mediaItems: Flow<PagingData<TmdbItem>> = Pager(PagingConfig(pageSize = ViewModelConstants.PAGING_SIZE)) {
|
||||
RecommendedMediaPagingSource(mediaType)
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.viewmodel
|
||||
|
||||
object ViewModelConstants {
|
||||
|
||||
const val PAGING_SIZE = 6
|
||||
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.FractionalThreshold
|
||||
import androidx.compose.material.ThresholdConfig
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.material3.*
|
||||
@@ -25,6 +27,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
@@ -187,8 +190,7 @@ fun TwoLineImageTextCard(
|
||||
modifier: Modifier = Modifier,
|
||||
subtitle: String? = null,
|
||||
imageUrl: String? = null,
|
||||
noDataImage: Int = R.drawable.placeholder,
|
||||
placeholder: Int = R.drawable.placeholder,
|
||||
placeholder: ImageVector = Icons.Filled.Person,
|
||||
titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
subtitleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
onItemClicked: () -> Unit = {}
|
||||
@@ -198,7 +200,6 @@ fun TwoLineImageTextCard(
|
||||
width = 120.dp,
|
||||
onClick = onItemClicked,
|
||||
url = imageUrl,
|
||||
noDataImage = noDataImage,
|
||||
placeholder = placeholder,
|
||||
title = title,
|
||||
elevation = 0.dp,
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package com.owenlejeune.tvtime.ui.components
|
||||
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.magnifier
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BrokenImage
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
@@ -19,9 +27,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
@@ -118,7 +128,7 @@ fun PagingPeoplePosterGrid(
|
||||
person?.let {
|
||||
PosterItem(
|
||||
url = TmdbUtils.getFullPersonImagePath(person.profilePath),
|
||||
noDataImage = R.drawable.no_person_photo,
|
||||
placeholder = Icons.Filled.Person,
|
||||
modifier = Modifier.padding(5.dp),
|
||||
onClick = {
|
||||
onClick(person.id)
|
||||
@@ -155,7 +165,7 @@ fun PeoplePosterGrid(
|
||||
listItems(peopleList.value) { person ->
|
||||
PosterItem(
|
||||
url = TmdbUtils.getFullPersonImagePath(person.profilePath),
|
||||
noDataImage = R.drawable.no_person_photo,
|
||||
placeholder = Icons.Filled.Person,
|
||||
modifier = Modifier.padding(5.dp),
|
||||
onClick = {
|
||||
onClick(person.id)
|
||||
@@ -209,6 +219,7 @@ fun PosterItem(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PosterItem(
|
||||
url: String?,
|
||||
@@ -216,59 +227,68 @@ fun PosterItem(
|
||||
width: Dp = POSTER_WIDTH,
|
||||
onClick: () -> Unit = {},
|
||||
enabled: Boolean = true,
|
||||
noDataImage: Int = R.drawable.placeholder,
|
||||
placeholder: Int = R.drawable.placeholder,
|
||||
placeholder: ImageVector = Icons.Filled.Movie,
|
||||
elevation: Dp = 8.dp,
|
||||
title: String?,
|
||||
overrideShowTitle: Boolean? = null,
|
||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||
) {
|
||||
var sizeImage by remember { mutableStateOf(IntSize.Zero) }
|
||||
Card(
|
||||
elevation = elevation,
|
||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = elevation),
|
||||
modifier = modifier
|
||||
.width(width = width)
|
||||
.wrapContentHeight(),
|
||||
shape = RoundedCornerShape(5.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.clickable(
|
||||
enabled = true,
|
||||
.wrapContentHeight()
|
||||
.clickable(
|
||||
enabled = enabled,
|
||||
onClick = onClick
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(5.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = Color.Transparent)
|
||||
) {
|
||||
var backgroundColor by remember { mutableStateOf(Color.Gray) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.height(height = POSTER_HEIGHT)
|
||||
.background(color = backgroundColor)
|
||||
.clip(RoundedCornerShape(5.dp))
|
||||
.onGloballyPositioned { sizeImage = it.size }
|
||||
) {
|
||||
var sizeImage by remember { mutableStateOf(IntSize.Zero) }
|
||||
var bgIcon by remember { mutableStateOf(placeholder) }
|
||||
Icon(
|
||||
imageVector = bgIcon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.focusable(enabled = false)
|
||||
.size(36.dp)
|
||||
.align(Alignment.Center),
|
||||
tint = MaterialTheme.colorScheme.background
|
||||
)
|
||||
|
||||
val gradient = Brush.verticalGradient(
|
||||
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.7f)),
|
||||
startY = sizeImage.height.toFloat() / 3f,
|
||||
endY = sizeImage.height.toFloat()
|
||||
)
|
||||
|
||||
if (url != null) {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.wrapContentHeight()
|
||||
.clip(RoundedCornerShape(5.dp))
|
||||
.onGloballyPositioned { sizeImage = it.size },
|
||||
onError = { Log.d("Poster", "Error loading: $url") },
|
||||
error = rememberAsyncImagePainter(model = noDataImage),
|
||||
model = url,
|
||||
placeholder = rememberAsyncImagePainter(model = placeholder),
|
||||
contentDescription = title,
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.height(height = POSTER_HEIGHT)
|
||||
.clip(RoundedCornerShape(5.dp))
|
||||
.onGloballyPositioned { sizeImage = it.size },
|
||||
painter = rememberAsyncImagePainter(model = noDataImage),
|
||||
contentDescription = title,
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
}
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.width(width = width)
|
||||
.wrapContentHeight()
|
||||
.clip(RoundedCornerShape(5.dp))
|
||||
.onGloballyPositioned { sizeImage = it.size },
|
||||
onError = {
|
||||
if (url != null) {
|
||||
bgIcon = Icons.Filled.BrokenImage
|
||||
Log.d("Poster", "Error loading: $url")
|
||||
}
|
||||
},
|
||||
model = url,
|
||||
contentDescription = title,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
onSuccess = { backgroundColor = Color.Transparent }
|
||||
)
|
||||
|
||||
val showTitle = overrideShowTitle ?: preferences.showPosterTitles
|
||||
if (showTitle) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
|
||||
import com.owenlejeune.tvtime.ui.screens.main.MediaTabContent
|
||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
||||
import com.owenlejeune.tvtime.ui.viewmodel.MediaTabViewModel
|
||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Response
|
||||
|
||||
@@ -22,7 +22,7 @@ fun MainNavGraph(
|
||||
appNavController: NavHostController,
|
||||
navController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||
startDestination: String = BottomNavItem.SortedItems[0].route
|
||||
) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
||||
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
|
||||
import com.owenlejeune.tvtime.ui.viewmodel.RecommendedMediaViewModel
|
||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.RecommendedMediaViewModel
|
||||
import com.owenlejeune.tvtime.utils.SessionManager
|
||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -50,7 +50,7 @@ import kotlin.reflect.KClass
|
||||
@Composable
|
||||
fun AccountTab(
|
||||
appNavController: NavHostController,
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||
doSignInPartTwo: Boolean = false
|
||||
) {
|
||||
@@ -60,7 +60,7 @@ fun AccountTab(
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
if (currentSession?.isAuthorized == false) {
|
||||
appBarTitle.value = stringResource(id = R.string.account_not_logged_in)
|
||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) }
|
||||
if (doSignInPartTwo) {
|
||||
AccountLoadingView()
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -72,13 +72,9 @@ fun AccountTab(
|
||||
} else {
|
||||
if (currentSession?.isAuthorized == true) {
|
||||
val accountDetails = remember { currentSession.accountDetails }
|
||||
appBarTitle.value =
|
||||
stringResource(
|
||||
id = R.string.account_header_title_formatted,
|
||||
getAccountName(accountDetails.value)
|
||||
)
|
||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_header_title_formatted, getAccountName(accountDetails.value))) }
|
||||
} else {
|
||||
appBarTitle.value = stringResource(id = R.string.account_not_logged_in)
|
||||
appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) }
|
||||
}
|
||||
|
||||
appBarActions.value = {
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -849,7 +850,6 @@ private fun CastCrewCard(appNavController: NavController, person: Person) {
|
||||
else -> null
|
||||
},
|
||||
imageUrl = TmdbUtils.getFullPersonImagePath(person),
|
||||
noDataImage = R.drawable.no_person_photo,
|
||||
titleTextColor = MaterialTheme.colorScheme.onPrimary,
|
||||
subtitleTextColor = MaterialTheme.colorScheme.onSecondary,
|
||||
onItemClicked = {
|
||||
@@ -899,7 +899,8 @@ fun SimilarContentCard(
|
||||
appNavController.navigate(
|
||||
"${MainNavItem.DetailView.route}/${mediaType}/${content.id}"
|
||||
)
|
||||
}
|
||||
},
|
||||
placeholder = Icons.Filled.Movie
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.owenlejeune.tvtime.ui.screens.main
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -17,25 +18,26 @@ import com.owenlejeune.tvtime.ui.components.SearchView
|
||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.Tabs
|
||||
import com.owenlejeune.tvtime.ui.viewmodel.MediaTabViewModel
|
||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
fun MediaTab(
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appNavController: NavHostController,
|
||||
mediaType: MediaViewType,
|
||||
fab: MutableState<@Composable () -> Unit>
|
||||
) {
|
||||
appBarTitle.value = when (mediaType) {
|
||||
val titleText = when (mediaType) {
|
||||
MediaViewType.MOVIE -> stringResource(id = R.string.nav_movies_title)
|
||||
MediaViewType.TV -> stringResource(id = R.string.nav_tv_title)
|
||||
else -> ""
|
||||
}
|
||||
appBarTitle.value = @Composable { Text(text = titleText) }
|
||||
|
||||
Column {
|
||||
SearchView(
|
||||
title = appBarTitle.value,
|
||||
title = titleText,
|
||||
appNavController = appNavController,
|
||||
mediaType = mediaType,
|
||||
fab = fab
|
||||
|
||||
@@ -49,7 +49,7 @@ fun PersonDetailView(
|
||||
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val scrollBehaviour = remember(decayAnimationSpec) {
|
||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
|
||||
TopAppBarDefaults.pinnedScrollBehavior(topAppBarScrollState)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@@ -58,7 +58,7 @@ fun PersonDetailView(
|
||||
SmallTopAppBar(
|
||||
scrollBehavior = scrollBehaviour,
|
||||
colors = TopAppBarDefaults
|
||||
.largeTopAppBarColors(
|
||||
.smallTopAppBarColors(
|
||||
scrolledContainerColor = MaterialTheme.colorScheme.background,
|
||||
titleContentColor = MaterialTheme.colorScheme.primary
|
||||
),
|
||||
|
||||
@@ -14,19 +14,20 @@ import com.owenlejeune.tvtime.R
|
||||
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
|
||||
import com.owenlejeune.tvtime.ui.components.SearchView
|
||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
||||
import com.owenlejeune.tvtime.ui.viewmodel.PeopleTabViewModel
|
||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.PeopleTabViewModel
|
||||
|
||||
@Composable
|
||||
fun PeopleTab(
|
||||
appBarTitle: MutableState<String>,
|
||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||
appNavController: NavHostController,
|
||||
fab: MutableState<@Composable () -> Unit>
|
||||
) {
|
||||
appBarTitle.value = stringResource(id = R.string.nav_people_title)
|
||||
val titleText = stringResource(id = R.string.nav_people_title)
|
||||
appBarTitle.value = { Text(text = titleText) }
|
||||
|
||||
Column {
|
||||
SearchView(
|
||||
title = appBarTitle.value,
|
||||
title = titleText,
|
||||
appNavController = appNavController,
|
||||
mediaType = MediaViewType.PERSON,
|
||||
fab = fab
|
||||
|
||||
5
app/src/main/res/drawable/ic_broken_image.xml
Normal file
5
app/src/main/res/drawable/ic_broken_image.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="@android:color/darker_gray"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21,5v6.59l-3,-3.01 -4,4.01 -4,-4 -4,4 -3,-3.01L3,5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01L21,19c0,1.1 -0.9,2 -2,2L5,21c-1.1,0 -2,-0.9 -2,-2v-6.58l3,2.99 4,-4 4,4 4,-3.99z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_rectangle.xml
Normal file
5
app/src/main/res/drawable/ic_rectangle.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="@android:color/darker_gray"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M2,4h20v16h-20z"/>
|
||||
</vector>
|
||||
@@ -212,4 +212,5 @@
|
||||
<string name="no_recommended_movies">No Recommended Movies</string>
|
||||
<string name="recommended_tv_title">Recommended TV</string>
|
||||
<string name="no_recommended_tv">No Recommended TV</string>
|
||||
<string name="no_result_found">No more results found</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user