refactor people view

This commit is contained in:
Owen LeJeune
2022-08-29 22:20:08 -04:00
parent 08ba3f1b08
commit db1059f7de
3 changed files with 118 additions and 216 deletions

View File

@@ -30,96 +30,8 @@ import com.owenlejeune.tvtime.ui.components.PosterItem
import com.owenlejeune.tvtime.ui.components.RatingRing
import com.owenlejeune.tvtime.utils.TmdbUtils
@Composable
fun DetailContent(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Box(modifier = modifier
.background(color = MaterialTheme.colorScheme.background)
.verticalScroll(rememberScrollState())
) {
content()
}
}
@Composable
fun DetailHeader(
appNavController: NavController,
title: String,
modifier: Modifier = Modifier,
backdropUrl: String? = null,
posterUrl: String? = null,
backdropContentDescription: String? = null,
posterContentDescription: String? = null,
rating: Float? = null
) {
ConstraintLayout(modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
) {
val (
backButton, backdropImage, posterImage, titleText, ratingsView
) = createRefs()
Backdrop(
modifier = Modifier
.constrainAs(backdropImage) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
imageUrl = backdropUrl,
contentDescription = backdropContentDescription
)
PosterItem(
modifier = Modifier
.constrainAs(posterImage) {
bottom.linkTo(backdropImage.bottom)
start.linkTo(parent.start, margin = 16.dp)
top.linkTo(backButton.bottom)
},
url = posterUrl,
contentDescription = posterContentDescription
)
TitleText(
modifier = Modifier
.constrainAs(titleText) {
bottom.linkTo(posterImage.bottom)
start.linkTo(posterImage.end, margin = 8.dp)
end.linkTo(parent.end, margin = 16.dp)
},
title = title
)
rating?.let {
RatingView(
modifier = Modifier
.constrainAs(ratingsView) {
bottom.linkTo(titleText.top)
start.linkTo(posterImage.end, margin = 20.dp)
},
progress = rating
)
}
BackButton(
modifier = Modifier.constrainAs(backButton) {
top.linkTo(parent.top)//, 8.dp)
start.linkTo(parent.start, 8.dp)
bottom.linkTo(posterImage.top)
},
appNavController = appNavController
)
}
}
@Composable
fun DetailHeader2(
appNavController: NavController,
title: String,
modifier: Modifier = Modifier,
backdropUrl: String? = null,
posterUrl: String? = null,
@@ -188,22 +100,6 @@ private fun Backdrop(modifier: Modifier, imageUrl: String?, contentDescription:
)
}
@Composable
private fun TitleText(modifier: Modifier, title: String) {
Text(
text = title,
color = MaterialTheme.colorScheme.primary,
modifier = modifier
.padding(start = 16.dp, end = 16.dp)
.fillMaxWidth(.6f),
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Start,
softWrap = true,
maxLines = 3,
overflow = TextOverflow.Ellipsis
)
}
@Composable
fun RatingView(
progress: Float,
@@ -225,23 +121,4 @@ fun RatingView(
size = 50.dp
)
}
}
@Composable
private fun BackButton(modifier: Modifier, appNavController: NavController) {
val start = if (isSystemInDarkTheme()) Color.Black else Color.White
IconButton(
onClick = { appNavController.popBackStack() },
modifier = modifier
.background(
brush = Brush.radialGradient(colors = listOf(start, Color.Transparent))
)
.wrapContentSize()
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(R.string.content_description_back_button),
tint = MaterialTheme.colorScheme.primary
)
}
}

View File

@@ -108,9 +108,7 @@ fun MediaDetailView(
.padding(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
DetailHeader2(
appNavController = appNavController,
title = mediaItem.value?.title ?: "",
DetailHeader(
posterUrl = TmdbUtils.getFullPosterPath(mediaItem.value?.posterPath),
posterContentDescription = mediaItem.value?.title,
backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem.value?.backdropPath),

View File

@@ -1,17 +1,20 @@
package com.owenlejeune.tvtime.ui.screens.main
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -30,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PersonDetailView(
appNavController: NavController,
@@ -42,108 +46,131 @@ fun PersonDetailView(
}
}
Column(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.background)
.verticalScroll(rememberScrollState())
.fillMaxSize()
.padding(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
DetailHeader(
appNavController = appNavController,
title = person.value?.name ?: "",
posterUrl = TmdbUtils.getFullPersonImagePath(person.value?.profilePath),
posterContentDescription = person.value?.name
)
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val topAppBarScrollState = rememberTopAppBarScrollState()
val scrollBehaviour = remember(decayAnimationSpec) {
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
}
Column(
modifier = Modifier.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
BiographyCard(person = person.value)
val credits = remember { mutableStateOf<PersonCreditsResponse?>(null) }
personId?.let {
if (credits.value == null) {
fetchCredits(personId, credits)
}
}
ContentCard(title = stringResource(R.string.known_for_label)) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
items(credits.value?.cast?.size ?: 0) { i ->
val content = credits.value!!.cast[i]
TwoLineImageTextCard(
title = content.name,
titleTextColor = MaterialTheme.colorScheme.primary,
subtitle = content.character,
modifier = Modifier
.width(124.dp)
.wrapContentHeight(),
imageUrl = TmdbUtils.getFullPosterPath(content.posterPath),
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
}
Scaffold(
modifier = Modifier.nestedScroll(scrollBehaviour.nestedScrollConnection),
topBar = {
SmallTopAppBar(
scrollBehavior = scrollBehaviour,
colors = TopAppBarDefaults
.largeTopAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.background,
titleContentColor = MaterialTheme.colorScheme.primary
),
title = { Text(text = person.value?.name ?: "") },
navigationIcon = {
IconButton(onClick = { appNavController.popBackStack() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(id = R.string.content_description_back_button),
tint = MaterialTheme.colorScheme.primary
)
}
}
}
)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Column(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.background)
.verticalScroll(state = rememberScrollState())
.padding(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
DetailHeader(
posterUrl = TmdbUtils.getFullPersonImagePath(person.value?.profilePath),
posterContentDescription = person.value?.profilePath
)
val departments = credits.value?.crew?.map { it.department }?.toSet() ?: emptySet()
if (departments.isNotEmpty()) {
ContentCard(title = stringResource(R.string.also_known_for_label)) {
Column(
BiographyCard(person = person.value)
val credits = remember { mutableStateOf<PersonCreditsResponse?>(null) }
personId?.let {
if (credits.value == null) {
fetchCredits(personId, credits)
}
}
ContentCard(title = stringResource(R.string.known_for_label)) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
departments.forEach { department ->
Text(text = department, color = MaterialTheme.colorScheme.primary)
LazyRow(
items(credits.value?.cast?.size ?: 0) { i ->
val content = credits.value!!.cast[i]
TwoLineImageTextCard(
title = content.name,
titleTextColor = MaterialTheme.colorScheme.primary,
subtitle = content.character,
modifier = Modifier
.fillMaxWidth()
.width(124.dp)
.wrapContentHeight(),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
val jobsInDepartment =
credits.value!!.crew.filter { it.department == department }
items(jobsInDepartment.size) { i ->
val content = jobsInDepartment[i]
val title = if (content.mediaType == MediaViewType.MOVIE) {
content.title ?: ""
} else {
content.name ?: ""
imageUrl = TmdbUtils.getFullPosterPath(content.posterPath),
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
TwoLineImageTextCard(
title = title,
subtitle = content.job,
modifier = Modifier
.width(124.dp)
.wrapContentHeight(),
imageUrl = TmdbUtils.getFullPosterPath(content.posterPath),
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
}
)
}
}
}
val departments = credits.value?.crew?.map { it.department }?.toSet() ?: emptySet()
if (departments.isNotEmpty()) {
ContentCard(title = stringResource(R.string.also_known_for_label)) {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
departments.forEach { department ->
Text(text = department, color = MaterialTheme.colorScheme.primary)
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
val jobsInDepartment =
credits.value!!.crew.filter { it.department == department }
items(jobsInDepartment.size) { i ->
val content = jobsInDepartment[i]
val title = if (content.mediaType == MediaViewType.MOVIE) {
content.title ?: ""
} else {
content.name ?: ""
}
)
TwoLineImageTextCard(
title = title,
subtitle = content.job,
modifier = Modifier
.width(124.dp)
.wrapContentHeight(),
imageUrl = TmdbUtils.getFullPosterPath(content.posterPath),
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
}
)
}
}
}
}