mirror of
https://github.com/owenlejeune/TVTime.git
synced 2026-02-19 11:26:51 -05:00
make reviews card collapsable on smaller screens
This commit is contained in:
@@ -76,6 +76,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedMovie
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedTv
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedTv
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Genre
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Genre
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Review
|
||||||
import com.owenlejeune.tvtime.extensions.DateFormat
|
import com.owenlejeune.tvtime.extensions.DateFormat
|
||||||
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||||
import com.owenlejeune.tvtime.extensions.combineWith
|
import com.owenlejeune.tvtime.extensions.combineWith
|
||||||
@@ -99,6 +100,7 @@ import com.owenlejeune.tvtime.ui.components.ChipStyle
|
|||||||
import com.owenlejeune.tvtime.ui.components.CircleBackgroundColorImage
|
import com.owenlejeune.tvtime.ui.components.CircleBackgroundColorImage
|
||||||
import com.owenlejeune.tvtime.ui.components.ContentCard
|
import com.owenlejeune.tvtime.ui.components.ContentCard
|
||||||
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ExpandableContentCard
|
||||||
import com.owenlejeune.tvtime.ui.components.ExternalIdsArea
|
import com.owenlejeune.tvtime.ui.components.ExternalIdsArea
|
||||||
import com.owenlejeune.tvtime.ui.components.HtmlText
|
import com.owenlejeune.tvtime.ui.components.HtmlText
|
||||||
import com.owenlejeune.tvtime.ui.components.ImageGalleryOverlay
|
import com.owenlejeune.tvtime.ui.components.ImageGalleryOverlay
|
||||||
@@ -385,7 +387,7 @@ fun MediaViewContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (windowSize != WindowSizeClass.Expanded) {
|
if (windowSize != WindowSizeClass.Expanded) {
|
||||||
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
|
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel, windowSize = windowSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,7 +402,7 @@ fun MediaViewContent(
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.verticalScroll(state = rememberScrollState())
|
.verticalScroll(state = rememberScrollState())
|
||||||
) {
|
) {
|
||||||
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
|
ReviewsCard(itemId = itemId, type = type, mainViewModel = mainViewModel, windowSize = windowSize)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
@@ -837,7 +839,12 @@ private fun SeasonCard(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(all = 12.dp)
|
.padding(all = 12.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
appNavController.navigate(AppNavItem.DetailView.withArgs(MediaViewType.SEASON, itemId.combineWith(it.seasonNumber)))
|
appNavController.navigate(
|
||||||
|
AppNavItem.DetailView.withArgs(
|
||||||
|
MediaViewType.SEASON,
|
||||||
|
itemId.combineWith(it.seasonNumber)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
PosterItem(
|
PosterItem(
|
||||||
@@ -1073,132 +1080,176 @@ private fun NextMcuProjectCard(
|
|||||||
private fun ReviewsCard(
|
private fun ReviewsCard(
|
||||||
itemId: Int,
|
itemId: Int,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
|
windowSize: WindowSizeClass,
|
||||||
mainViewModel: MainViewModel,
|
mainViewModel: MainViewModel,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val reviewsMap = remember { mainViewModel.produceReviewsFor(type) }
|
val reviewsMap = remember { mainViewModel.produceReviewsFor(type) }
|
||||||
val reviews = reviewsMap[itemId]
|
val reviews = reviewsMap[itemId]
|
||||||
|
|
||||||
ListContentCard(
|
if (windowSize == WindowSizeClass.Expanded) {
|
||||||
modifier = modifier,
|
ListContentCard(
|
||||||
header = {
|
modifier = modifier,
|
||||||
Column(
|
header = {
|
||||||
verticalArrangement = Arrangement.spacedBy(9.dp)
|
ReviewsCardHeader()
|
||||||
) {
|
},
|
||||||
Text(
|
) {
|
||||||
text = stringResource(R.string.reviews_title),
|
ReviewsCardContent(reviews = reviews)
|
||||||
style = MaterialTheme.typography.titleLarge,
|
}
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
} else {
|
||||||
)
|
ExpandableContentCard(
|
||||||
|
modifier = modifier,
|
||||||
if (SessionManager.currentSession.value?.isAuthorized == true) {
|
title = {
|
||||||
Row(
|
Box(
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(50.dp)
|
|
||||||
.padding(bottom = 4.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
var reviewTextState by remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
RoundedTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(40.dp)
|
|
||||||
.align(Alignment.CenterVertically)
|
|
||||||
.weight(1f),
|
|
||||||
value = reviewTextState,
|
|
||||||
onValueChange = { reviewTextState = it },
|
|
||||||
placeHolder = stringResource(R.string.add_a_review_hint),
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.secondary,
|
|
||||||
placeHolderTextColor = MaterialTheme.colorScheme.background,
|
|
||||||
textColor = MaterialTheme.colorScheme.onSecondary
|
|
||||||
)
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
CircleBackgroundColorImage(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.CenterVertically)
|
|
||||||
.clickable(
|
|
||||||
onClick = {
|
|
||||||
Toast
|
|
||||||
.makeText(context, "TODO", Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
size = 40.dp,
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.tertiary,
|
|
||||||
image = Icons.Filled.Send,
|
|
||||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.surfaceVariant),
|
|
||||||
contentDescription = ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (reviews?.isNotEmpty() == true) {
|
|
||||||
reviews.reversed().forEachIndexed { index, review ->
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(horizontal = 16.dp)
|
||||||
.padding(end = 16.dp),
|
.padding(top = 16.dp)
|
||||||
verticalAlignment = Alignment.Top,
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
) {
|
||||||
AvatarImage(
|
ReviewsCardHeader()
|
||||||
size = 50.dp,
|
|
||||||
author = review.authorDetails
|
|
||||||
)
|
|
||||||
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = review.author,
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
|
|
||||||
HtmlText(
|
|
||||||
text = review.content,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
|
|
||||||
val createdAt = TmdbUtils.formatDate(review.createdAt)
|
|
||||||
val updatedAt = TmdbUtils.formatDate(review.updatedAt)
|
|
||||||
var timestamp = stringResource(id = R.string.created_at_label, createdAt)
|
|
||||||
if (updatedAt != createdAt) {
|
|
||||||
timestamp += "\n${stringResource(id = R.string.updated_at_label, updatedAt)}"
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = timestamp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
fontSize = 12.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index != reviews.size - 1) {
|
|
||||||
Divider(
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
modifier = Modifier.padding(vertical = 12.dp)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
) { isExpanded ->
|
||||||
Text(
|
val reviewsSubList = reviews?.let {
|
||||||
|
if (isExpanded) {
|
||||||
|
reviews
|
||||||
|
} else {
|
||||||
|
reviews.subList(0, 3)
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.padding(16.dp),
|
||||||
.padding(horizontal = 24.dp, vertical = 22.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
text = stringResource(R.string.no_reviews_label),
|
) {
|
||||||
color = MaterialTheme.colorScheme.tertiary,
|
ReviewsCardContent(reviews = reviewsSubList)
|
||||||
textAlign = TextAlign.Center,
|
}
|
||||||
style = MaterialTheme.typography.headlineMedium
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ReviewsCardHeader() {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(9.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.reviews_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
if (SessionManager.currentSession.value?.isAuthorized == true) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(50.dp)
|
||||||
|
.padding(bottom = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
var reviewTextState by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
RoundedTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(40.dp)
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.weight(1f),
|
||||||
|
value = reviewTextState,
|
||||||
|
onValueChange = { reviewTextState = it },
|
||||||
|
placeHolder = stringResource(R.string.add_a_review_hint),
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.secondary,
|
||||||
|
placeHolderTextColor = MaterialTheme.colorScheme.background,
|
||||||
|
textColor = MaterialTheme.colorScheme.onSecondary
|
||||||
|
)
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
CircleBackgroundColorImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.clickable(
|
||||||
|
onClick = {
|
||||||
|
Toast
|
||||||
|
.makeText(context, "TODO", Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
size = 40.dp,
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.tertiary,
|
||||||
|
image = Icons.Filled.Send,
|
||||||
|
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.surfaceVariant),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ReviewsCardContent(
|
||||||
|
reviews: List<Review>?
|
||||||
|
) {
|
||||||
|
if (reviews?.isNotEmpty() == true) {
|
||||||
|
reviews.reversed().forEachIndexed { index, review ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(end = 16.dp),
|
||||||
|
verticalAlignment = Alignment.Top,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
AvatarImage(
|
||||||
|
size = 50.dp,
|
||||||
|
author = review.authorDetails
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = review.author,
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
|
||||||
|
HtmlText(
|
||||||
|
text = review.content,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
val createdAt = TmdbUtils.formatDate(review.createdAt)
|
||||||
|
val updatedAt = TmdbUtils.formatDate(review.updatedAt)
|
||||||
|
var timestamp = stringResource(id = R.string.created_at_label, createdAt)
|
||||||
|
if (updatedAt != createdAt) {
|
||||||
|
timestamp += "\n${stringResource(id = R.string.updated_at_label, updatedAt)}"
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = timestamp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
fontSize = 12.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index != reviews.size - 1) {
|
||||||
|
Divider(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
modifier = Modifier.padding(vertical = 12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(horizontal = 24.dp, vertical = 22.dp),
|
||||||
|
text = stringResource(R.string.no_reviews_label),
|
||||||
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailsFor(
|
fun DetailsFor(
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
|
|||||||
Reference in New Issue
Block a user