mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-09 13:22:44 -05:00
add additional details to person known for
This commit is contained in:
@@ -20,7 +20,7 @@ class DateTypeAdapter: TypeAdapter<Date>() {
|
|||||||
override fun read(jrIn: JsonReader): Date? {
|
override fun read(jrIn: JsonReader): Date? {
|
||||||
if (jrIn.peek() == JsonToken.NULL) {
|
if (jrIn.peek() == JsonToken.NULL) {
|
||||||
jrIn.nextNull()
|
jrIn.nextNull()
|
||||||
throw Exception("JSON must not be null")
|
return null
|
||||||
}
|
}
|
||||||
val dateFields = jrIn.nextString()
|
val dateFields = jrIn.nextString()
|
||||||
if (dateFields.isEmpty()) {
|
if (dateFields.isEmpty()) {
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.utils.types.Gender
|
import com.owenlejeune.tvtime.utils.types.Gender
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class DetailPerson(
|
class DetailPerson(
|
||||||
@SerializedName("birthday") val birthday: String,
|
@SerializedName("birthday") val birthday: Date,
|
||||||
@SerializedName("known_for_department") val knownFor: String,
|
@SerializedName("known_for_department") val knownFor: String,
|
||||||
@SerializedName("deathday") val dateOfDeath: String?,
|
@SerializedName("deathday") val dateOfDeath: Date?,
|
||||||
@SerializedName("biography") val biography: String,
|
@SerializedName("biography") val biography: String,
|
||||||
@SerializedName("place_of_birth") val birthplace: String?,
|
@SerializedName("place_of_birth") val birthplace: String?,
|
||||||
@SerializedName("adult") val isAdult: Boolean,
|
@SerializedName("adult") val isAdult: Boolean,
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.owenlejeune.tvtime.extensions
|
package com.owenlejeune.tvtime.extensions
|
||||||
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.Period
|
||||||
|
import java.time.ZoneId
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -16,6 +20,18 @@ fun Date.format(format: DateFormat): String {
|
|||||||
return formatter.format(this)
|
return formatter.format(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DateFormat(val format: String) {
|
fun Date.toLocalDate() = Instant.ofEpochMilli(time)
|
||||||
MMMM_dd("MMMM dd")
|
.atZone(ZoneId.systemDefault())
|
||||||
|
.toLocalDate()
|
||||||
|
|
||||||
|
fun Date.yearsSince(anchorDate: Date? = null): Int {
|
||||||
|
val endDate = anchorDate?.toLocalDate() ?: LocalDate.now()
|
||||||
|
val thisLocalDate = toLocalDate()
|
||||||
|
|
||||||
|
return Period.between(thisLocalDate, endDate).years
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DateFormat(val format: String) {
|
||||||
|
MMMM_dd("MMMM dd"),
|
||||||
|
MMMM_dd_yyyy("MMMM dd yyyy")
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,11 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -26,6 +28,7 @@ import androidx.compose.ui.semantics.semantics
|
|||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil.compose.rememberAsyncImagePainter
|
||||||
@@ -332,3 +335,30 @@ fun PlaceholderDetailHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdditionalDetailItem(
|
||||||
|
title: String,
|
||||||
|
subtext: String,
|
||||||
|
includeDivider: Boolean = true
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = subtext,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
if (includeDivider) {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,6 +99,7 @@ import com.owenlejeune.tvtime.extensions.listItems
|
|||||||
import com.owenlejeune.tvtime.extensions.shimmerBackground
|
import com.owenlejeune.tvtime.extensions.shimmerBackground
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.components.ActionsView
|
import com.owenlejeune.tvtime.ui.components.ActionsView
|
||||||
|
import com.owenlejeune.tvtime.ui.components.AdditionalDetailItem
|
||||||
import com.owenlejeune.tvtime.ui.components.AvatarImage
|
import com.owenlejeune.tvtime.ui.components.AvatarImage
|
||||||
import com.owenlejeune.tvtime.ui.components.BackButton
|
import com.owenlejeune.tvtime.ui.components.BackButton
|
||||||
import com.owenlejeune.tvtime.ui.components.ChipDefaults
|
import com.owenlejeune.tvtime.ui.components.ChipDefaults
|
||||||
@@ -796,34 +797,6 @@ private fun AdditionalTvItems(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AdditionalDetailItem(
|
|
||||||
title: String,
|
|
||||||
subtext: String,
|
|
||||||
includeDivider: Boolean = true
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
fontSize = 16.sp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = subtext,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
|
||||||
)
|
|
||||||
if (includeDivider) {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CastCard(
|
private fun CastCard(
|
||||||
itemId: Int,
|
itemId: Int,
|
||||||
|
|||||||
@@ -46,7 +46,13 @@ import androidx.navigation.NavController
|
|||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedMovie
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedTv
|
||||||
|
import com.owenlejeune.tvtime.extensions.DateFormat
|
||||||
import com.owenlejeune.tvtime.extensions.combinedOnVisibilityChange
|
import com.owenlejeune.tvtime.extensions.combinedOnVisibilityChange
|
||||||
|
import com.owenlejeune.tvtime.extensions.format
|
||||||
|
import com.owenlejeune.tvtime.extensions.yearsSince
|
||||||
|
import com.owenlejeune.tvtime.ui.components.AdditionalDetailItem
|
||||||
import com.owenlejeune.tvtime.ui.components.BackButton
|
import com.owenlejeune.tvtime.ui.components.BackButton
|
||||||
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
|
||||||
@@ -173,6 +179,8 @@ fun PersonDetailScreen(
|
|||||||
|
|
||||||
CreditsCard(personId = personId, appNavController = appNavController)
|
CreditsCard(personId = personId, appNavController = appNavController)
|
||||||
|
|
||||||
|
AdditionalDetailsCard(id = personId, mainViewModel = mainViewModel)
|
||||||
|
|
||||||
ImagesCard(id = personId, appNavController = appNavController)
|
ImagesCard(id = personId, appNavController = appNavController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +214,49 @@ private fun BiographyCard(person: DetailPerson?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AdditionalDetailsCard(
|
||||||
|
id: Int,
|
||||||
|
mainViewModel: MainViewModel,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val personMap = remember { mainViewModel.peopleMap }
|
||||||
|
val person = personMap[id]
|
||||||
|
|
||||||
|
ContentCard(
|
||||||
|
modifier = modifier,
|
||||||
|
title = stringResource(R.string.additional_details_title)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(vertical = 12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
AdditionalDetailItem(
|
||||||
|
title = stringResource(R.string.also_known_as),
|
||||||
|
subtext = person?.alsoKnownAs?.joinToString(separator = ", ") ?: ""
|
||||||
|
)
|
||||||
|
AdditionalDetailItem(
|
||||||
|
title = stringResource(R.string.place_of_birth),
|
||||||
|
subtext = person?.birthplace ?: ""
|
||||||
|
)
|
||||||
|
val birthday = person?.birthday
|
||||||
|
AdditionalDetailItem(
|
||||||
|
title = stringResource(R.string.birthday),
|
||||||
|
subtext = "${birthday?.format(DateFormat.MMMM_dd_yyyy) ?: ""} (${birthday?.yearsSince(person.dateOfDeath)} years old)"
|
||||||
|
)
|
||||||
|
person?.dateOfDeath?.let {
|
||||||
|
AdditionalDetailItem(
|
||||||
|
title = stringResource(R.string.date_of_death),
|
||||||
|
subtext = it.format(DateFormat.MMMM_dd_yyyy)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CreditsCard(
|
private fun CreditsCard(
|
||||||
personId: Int,
|
personId: Int,
|
||||||
|
|||||||
@@ -256,4 +256,8 @@
|
|||||||
<string name="see_all_cast_and_crew">See all cast and crew</string>
|
<string name="see_all_cast_and_crew">See all cast and crew</string>
|
||||||
<string name="unreleased">Unreleased</string>
|
<string name="unreleased">Unreleased</string>
|
||||||
<string name="tba">TBA</string>
|
<string name="tba">TBA</string>
|
||||||
|
<string name="also_known_as">Also Known As</string>
|
||||||
|
<string name="place_of_birth">Place of birth</string>
|
||||||
|
<string name="birthday">Birthday</string>
|
||||||
|
<string name="date_of_death">Date of death</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user