mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-04 01:09:05 +00:00
fix: using unique key for transition
This commit is contained in:
parent
81af639fe9
commit
fa7e3aabfc
5 changed files with 38 additions and 15 deletions
|
|
@ -171,6 +171,7 @@ data class PersonDetailRoute(
|
||||||
val personId: Int,
|
val personId: Int,
|
||||||
val personName: String,
|
val personName: String,
|
||||||
val personPhoto: String? = null,
|
val personPhoto: String? = null,
|
||||||
|
val castAvatarTransitionKey: String? = null,
|
||||||
val preferCrew: Boolean = false,
|
val preferCrew: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -766,7 +767,7 @@ private fun MainAppContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCastClick = { person ->
|
onCastClick = { person, avatarTransitionKey ->
|
||||||
val tmdbId = person.tmdbId
|
val tmdbId = person.tmdbId
|
||||||
if (tmdbId != null && tmdbId > 0) {
|
if (tmdbId != null && tmdbId > 0) {
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
|
|
@ -774,6 +775,7 @@ private fun MainAppContent(
|
||||||
personId = tmdbId,
|
personId = tmdbId,
|
||||||
personName = person.name,
|
personName = person.name,
|
||||||
personPhoto = person.photo,
|
personPhoto = person.photo,
|
||||||
|
castAvatarTransitionKey = avatarTransitionKey,
|
||||||
preferCrew = person.role?.let {
|
preferCrew = person.role?.let {
|
||||||
it.equals("Director", ignoreCase = true) ||
|
it.equals("Director", ignoreCase = true) ||
|
||||||
it.equals("Writer", ignoreCase = true) ||
|
it.equals("Writer", ignoreCase = true) ||
|
||||||
|
|
@ -807,6 +809,7 @@ private fun MainAppContent(
|
||||||
personId = route.personId,
|
personId = route.personId,
|
||||||
personName = route.personName,
|
personName = route.personName,
|
||||||
initialProfilePhoto = route.personPhoto,
|
initialProfilePhoto = route.personPhoto,
|
||||||
|
avatarTransitionKey = route.castAvatarTransitionKey,
|
||||||
preferCrew = route.preferCrew,
|
preferCrew = route.preferCrew,
|
||||||
onBack = { navController.popBackStack() },
|
onBack = { navController.popBackStack() },
|
||||||
onOpenMeta = { preview ->
|
onOpenMeta = { preview ->
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
package com.nuvio.app.features.details
|
package com.nuvio.app.features.details
|
||||||
|
|
||||||
internal fun castAvatarSharedTransitionKey(personId: Int): String = "cast-avatar:$personId"
|
internal fun castAvatarSharedTransitionKey(
|
||||||
|
personId: Int,
|
||||||
|
occurrenceIndex: Int? = null,
|
||||||
|
): String =
|
||||||
|
if (occurrenceIndex != null) {
|
||||||
|
"cast-avatar:$personId:$occurrenceIndex"
|
||||||
|
} else {
|
||||||
|
"cast-avatar:$personId"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ fun MetaDetailsScreen(
|
||||||
onPlay: ((type: String, videoId: String, parentMetaId: String, parentMetaType: String, title: String, logo: String?, poster: String?, background: String?, seasonNumber: Int?, episodeNumber: Int?, episodeTitle: String?, episodeThumbnail: String?, pauseDescription: String?, resumePositionMs: Long?) -> Unit)? = null,
|
onPlay: ((type: String, videoId: String, parentMetaId: String, parentMetaType: String, title: String, logo: String?, poster: String?, background: String?, seasonNumber: Int?, episodeNumber: Int?, episodeTitle: String?, episodeThumbnail: String?, pauseDescription: String?, resumePositionMs: Long?) -> Unit)? = null,
|
||||||
onPlayManually: ((type: String, videoId: String, parentMetaId: String, parentMetaType: String, title: String, logo: String?, poster: String?, background: String?, seasonNumber: Int?, episodeNumber: Int?, episodeTitle: String?, episodeThumbnail: String?, pauseDescription: String?, resumePositionMs: Long?) -> Unit)? = null,
|
onPlayManually: ((type: String, videoId: String, parentMetaId: String, parentMetaType: String, title: String, logo: String?, poster: String?, background: String?, seasonNumber: Int?, episodeNumber: Int?, episodeTitle: String?, episodeThumbnail: String?, pauseDescription: String?, resumePositionMs: Long?) -> Unit)? = null,
|
||||||
onOpenMeta: ((MetaPreview) -> Unit)? = null,
|
onOpenMeta: ((MetaPreview) -> Unit)? = null,
|
||||||
onCastClick: ((MetaPerson) -> Unit)? = null,
|
onCastClick: ((MetaPerson, String?) -> Unit)? = null,
|
||||||
onCompanyClick: ((MetaCompany, String) -> Unit)? = null,
|
onCompanyClick: ((MetaCompany, String) -> Unit)? = null,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
|
|
@ -919,7 +919,7 @@ private fun ConfiguredMetaSections(
|
||||||
onEpisodeClick: (MetaVideo) -> Unit,
|
onEpisodeClick: (MetaVideo) -> Unit,
|
||||||
onEpisodeLongPress: (MetaVideo) -> Unit,
|
onEpisodeLongPress: (MetaVideo) -> Unit,
|
||||||
onOpenMeta: ((MetaPreview) -> Unit)?,
|
onOpenMeta: ((MetaPreview) -> Unit)?,
|
||||||
onCastClick: ((MetaPerson) -> Unit)?,
|
onCastClick: ((MetaPerson, String?) -> Unit)?,
|
||||||
onCompanyClick: ((MetaCompany, String) -> Unit)?,
|
onCompanyClick: ((MetaCompany, String) -> Unit)?,
|
||||||
sharedTransitionScope: SharedTransitionScope?,
|
sharedTransitionScope: SharedTransitionScope?,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope?,
|
animatedVisibilityScope: AnimatedVisibilityScope?,
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ fun PersonDetailScreen(
|
||||||
personId: Int,
|
personId: Int,
|
||||||
personName: String,
|
personName: String,
|
||||||
initialProfilePhoto: String? = null,
|
initialProfilePhoto: String? = null,
|
||||||
|
avatarTransitionKey: String? = null,
|
||||||
preferCrew: Boolean = false,
|
preferCrew: Boolean = false,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onOpenMeta: (MetaPreview) -> Unit,
|
onOpenMeta: (MetaPreview) -> Unit,
|
||||||
|
|
@ -81,6 +82,7 @@ fun PersonDetailScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var uiState by remember(personId) { mutableStateOf<PersonDetailUiState>(PersonDetailUiState.Loading) }
|
var uiState by remember(personId) { mutableStateOf<PersonDetailUiState>(PersonDetailUiState.Loading) }
|
||||||
|
val resolvedAvatarTransitionKey = avatarTransitionKey ?: castAvatarSharedTransitionKey(personId)
|
||||||
|
|
||||||
LaunchedEffect(personId) {
|
LaunchedEffect(personId) {
|
||||||
uiState = PersonDetailUiState.Loading
|
uiState = PersonDetailUiState.Loading
|
||||||
|
|
@ -105,6 +107,7 @@ fun PersonDetailScreen(
|
||||||
personId = personId,
|
personId = personId,
|
||||||
personName = personName,
|
personName = personName,
|
||||||
profilePhoto = initialProfilePhoto,
|
profilePhoto = initialProfilePhoto,
|
||||||
|
avatarTransitionKey = resolvedAvatarTransitionKey,
|
||||||
sharedTransitionScope = sharedTransitionScope,
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
@ -119,6 +122,7 @@ fun PersonDetailScreen(
|
||||||
person = state.personDetail,
|
person = state.personDetail,
|
||||||
onOpenMeta = onOpenMeta,
|
onOpenMeta = onOpenMeta,
|
||||||
initialProfilePhoto = initialProfilePhoto,
|
initialProfilePhoto = initialProfilePhoto,
|
||||||
|
avatarTransitionKey = resolvedAvatarTransitionKey,
|
||||||
sharedTransitionScope = sharedTransitionScope,
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
@ -147,6 +151,7 @@ private fun PersonDetailContent(
|
||||||
person: PersonDetail,
|
person: PersonDetail,
|
||||||
onOpenMeta: (MetaPreview) -> Unit,
|
onOpenMeta: (MetaPreview) -> Unit,
|
||||||
initialProfilePhoto: String? = null,
|
initialProfilePhoto: String? = null,
|
||||||
|
avatarTransitionKey: String,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
) {
|
) {
|
||||||
|
|
@ -240,6 +245,7 @@ private fun PersonDetailContent(
|
||||||
person = person,
|
person = person,
|
||||||
collapseProgress = collapseProgress,
|
collapseProgress = collapseProgress,
|
||||||
fallbackProfilePhoto = initialProfilePhoto,
|
fallbackProfilePhoto = initialProfilePhoto,
|
||||||
|
avatarTransitionKey = avatarTransitionKey,
|
||||||
sharedTransitionScope = sharedTransitionScope,
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
@ -292,6 +298,7 @@ private fun HeroSection(
|
||||||
person: PersonDetail,
|
person: PersonDetail,
|
||||||
collapseProgress: Float = 0f,
|
collapseProgress: Float = 0f,
|
||||||
fallbackProfilePhoto: String? = null,
|
fallbackProfilePhoto: String? = null,
|
||||||
|
avatarTransitionKey: String,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
) {
|
) {
|
||||||
|
|
@ -299,7 +306,7 @@ private fun HeroSection(
|
||||||
val heroScale = 1f - (collapseProgress * 0.12f)
|
val heroScale = 1f - (collapseProgress * 0.12f)
|
||||||
val heroAlpha = 1f - (collapseProgress * 0.35f)
|
val heroAlpha = 1f - (collapseProgress * 0.35f)
|
||||||
val avatarUrl = person.profilePhoto?.takeIf { it.isNotBlank() } ?: fallbackProfilePhoto
|
val avatarUrl = person.profilePhoto?.takeIf { it.isNotBlank() } ?: fallbackProfilePhoto
|
||||||
val avatarCacheKey = castAvatarSharedTransitionKey(person.tmdbId)
|
val avatarCacheKey = avatarTransitionKey
|
||||||
val platformContext = LocalPlatformContext.current
|
val platformContext = LocalPlatformContext.current
|
||||||
val avatarRequest = if (!avatarUrl.isNullOrBlank()) {
|
val avatarRequest = if (!avatarUrl.isNullOrBlank()) {
|
||||||
remember(platformContext, avatarUrl, avatarCacheKey) {
|
remember(platformContext, avatarUrl, avatarCacheKey) {
|
||||||
|
|
@ -317,7 +324,7 @@ private fun HeroSection(
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
Modifier.sharedElement(
|
Modifier.sharedElement(
|
||||||
sharedContentState = rememberSharedContentState(
|
sharedContentState = rememberSharedContentState(
|
||||||
key = castAvatarSharedTransitionKey(person.tmdbId),
|
key = avatarTransitionKey,
|
||||||
),
|
),
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
@ -434,11 +441,12 @@ private fun PersonDetailSkeleton(
|
||||||
personId: Int,
|
personId: Int,
|
||||||
personName: String,
|
personName: String,
|
||||||
profilePhoto: String? = null,
|
profilePhoto: String? = null,
|
||||||
|
avatarTransitionKey: String,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
) {
|
) {
|
||||||
val accentColor = MaterialTheme.colorScheme.primary
|
val accentColor = MaterialTheme.colorScheme.primary
|
||||||
val avatarCacheKey = castAvatarSharedTransitionKey(personId)
|
val avatarCacheKey = avatarTransitionKey
|
||||||
val platformContext = LocalPlatformContext.current
|
val platformContext = LocalPlatformContext.current
|
||||||
val avatarRequest = if (!profilePhoto.isNullOrBlank()) {
|
val avatarRequest = if (!profilePhoto.isNullOrBlank()) {
|
||||||
remember(platformContext, profilePhoto, avatarCacheKey) {
|
remember(platformContext, profilePhoto, avatarCacheKey) {
|
||||||
|
|
@ -492,7 +500,7 @@ private fun PersonDetailSkeleton(
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
Modifier.sharedElement(
|
Modifier.sharedElement(
|
||||||
sharedContentState = rememberSharedContentState(
|
sharedContentState = rememberSharedContentState(
|
||||||
key = castAvatarSharedTransitionKey(personId),
|
key = avatarTransitionKey,
|
||||||
),
|
),
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ fun DetailCastSection(
|
||||||
cast: List<MetaPerson>,
|
cast: List<MetaPerson>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showHeader: Boolean = true,
|
showHeader: Boolean = true,
|
||||||
onCastClick: ((MetaPerson) -> Unit)? = null,
|
onCastClick: ((MetaPerson, String?) -> Unit)? = null,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
) {
|
) {
|
||||||
|
|
@ -61,14 +61,18 @@ fun DetailCastSection(
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
items = cast,
|
items = cast,
|
||||||
key = { index, person -> "${person.name}-${person.role.orEmpty()}-${person.photo.orEmpty()}-$index" },
|
key = { index, person -> "${person.name}-${person.role.orEmpty()}-${person.photo.orEmpty()}-$index" },
|
||||||
) { _, person ->
|
) { index, person ->
|
||||||
|
val sharedTransitionKey = person.tmdbId
|
||||||
|
?.takeIf { it > 0 }
|
||||||
|
?.let { castAvatarSharedTransitionKey(it, occurrenceIndex = index) }
|
||||||
CastItem(
|
CastItem(
|
||||||
person = person,
|
person = person,
|
||||||
|
sharedTransitionKey = sharedTransitionKey,
|
||||||
sizing = sizing,
|
sizing = sizing,
|
||||||
sharedTransitionScope = sharedTransitionScope,
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
onClick = if (onCastClick != null && person.tmdbId != null && person.tmdbId > 0) {
|
onClick = if (onCastClick != null && person.tmdbId != null && person.tmdbId > 0) {
|
||||||
{ onCastClick(person) }
|
{ onCastClick(person, sharedTransitionKey) }
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
|
@ -84,12 +88,13 @@ fun DetailCastSection(
|
||||||
private fun CastItem(
|
private fun CastItem(
|
||||||
person: MetaPerson,
|
person: MetaPerson,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
sharedTransitionKey: String? = null,
|
||||||
sizing: CastSectionSizing,
|
sizing: CastSectionSizing,
|
||||||
sharedTransitionScope: SharedTransitionScope? = null,
|
sharedTransitionScope: SharedTransitionScope? = null,
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val avatarCacheKey = person.tmdbId?.takeIf { it > 0 }?.let(::castAvatarSharedTransitionKey)
|
val avatarCacheKey = sharedTransitionKey
|
||||||
val platformContext = LocalPlatformContext.current
|
val platformContext = LocalPlatformContext.current
|
||||||
val avatarRequest = if (!person.photo.isNullOrBlank() && !avatarCacheKey.isNullOrBlank()) {
|
val avatarRequest = if (!person.photo.isNullOrBlank() && !avatarCacheKey.isNullOrBlank()) {
|
||||||
remember(platformContext, person.photo, avatarCacheKey) {
|
remember(platformContext, person.photo, avatarCacheKey) {
|
||||||
|
|
@ -107,13 +112,12 @@ private fun CastItem(
|
||||||
val avatarSharedElementModifier = if (
|
val avatarSharedElementModifier = if (
|
||||||
sharedTransitionScope != null &&
|
sharedTransitionScope != null &&
|
||||||
animatedVisibilityScope != null &&
|
animatedVisibilityScope != null &&
|
||||||
person.tmdbId != null &&
|
!sharedTransitionKey.isNullOrBlank()
|
||||||
person.tmdbId > 0
|
|
||||||
) {
|
) {
|
||||||
with(sharedTransitionScope) {
|
with(sharedTransitionScope) {
|
||||||
Modifier.sharedElement(
|
Modifier.sharedElement(
|
||||||
sharedContentState = rememberSharedContentState(
|
sharedContentState = rememberSharedContentState(
|
||||||
key = castAvatarSharedTransitionKey(person.tmdbId),
|
key = sharedTransitionKey,
|
||||||
),
|
),
|
||||||
animatedVisibilityScope = animatedVisibilityScope,
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue