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.ui.components.RatingRing
import com.owenlejeune.tvtime.utils.TmdbUtils 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 @Composable
fun DetailHeader( 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, modifier: Modifier = Modifier,
backdropUrl: String? = null, backdropUrl: String? = null,
posterUrl: 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 @Composable
fun RatingView( fun RatingView(
progress: Float, progress: Float,
@@ -226,22 +122,3 @@ fun RatingView(
) )
} }
} }
@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), .padding(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
DetailHeader2( DetailHeader(
appNavController = appNavController,
title = mediaItem.value?.title ?: "",
posterUrl = TmdbUtils.getFullPosterPath(mediaItem.value?.posterPath), posterUrl = TmdbUtils.getFullPosterPath(mediaItem.value?.posterPath),
posterContentDescription = mediaItem.value?.title, posterContentDescription = mediaItem.value?.title,
backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem.value?.backdropPath), backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem.value?.backdropPath),

View File

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