package com.craigvg.lichun_android.ui.navigation import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionScope import androidx.compose.runtime.Composable import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier import androidx.navigation3.ui.LocalNavAnimatedContentScope /** * Plumbing for Navigation 3 shared-element transitions. * * [SharedTransitionLayout][androidx.compose.animation.SharedTransitionLayout] in * [MainNavigation] exposes a [SharedTransitionScope]; that scope is published here so any * descendant composable (a list card on the source screen, an avatar on the destination * screen) can opt into a shared-element transition without threading the scope through every * call site. * * The per-destination [androidx.compose.animation.AnimatedVisibilityScope] is read from * Nav3's [LocalNavAnimatedContentScope]. * * All helpers degrade gracefully: when either scope is absent (e.g. a screen rendered in an * `@Preview`, inside a modal, or outside the [NavDisplay][androidx.navigation3.ui.NavDisplay]), * the modifier is a no-op so the same composables stay reusable everywhere. */ val LocalSharedTransitionScope = compositionLocalOf { null } /** Stable shared-element key for a character avatar shared into [PersonDetailKey]. */ fun personAvatarSharedKey(personId: String): String = "person-avatar-$personId" /** Stable shared-element key for a dating profile card shared into [RelationshipDetailKey]. */ fun datingProfileSharedKey(personId: String): String = "dating-profile-$personId" /** Stable shared-element key for an activity icon shared into [ActivityDetailKey]. */ fun activityIconSharedKey(activityId: String): String = "activity-icon-$activityId" /** * Tags this composable as one half of a shared-element transition identified by [key]. * * Matching keys on the source and destination composables produce the seamless * grow/shrink animation as the user navigates between them. Safe to call when no * shared-transition scope is in scope — it simply returns [this] unchanged. */ @OptIn(ExperimentalSharedTransitionApi::class) @Composable fun Modifier.sharedElementKey(key: String): Modifier { val sharedScope = LocalSharedTransitionScope.current ?: return this val animatedScope = LocalNavAnimatedContentScope.current with(sharedScope) { return this@sharedElementKey.sharedElement( sharedContentState = rememberSharedContentState(key), animatedVisibilityScope = animatedScope ) } }