package com.suno.android.common_ui.components import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.ContentTransform import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.scaleIn import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.suno.android.common_core_utils.model.AsyncData import com.suno.android.common_ui.components.loading.SunoSpinner import com.suno.android.common_ui.components.preview.SunoPreview import com.suno.android.common_ui.theme.ExtendedTheme @Composable @Suppress("MagicNumber") fun AnimatedAsyncDataContent( targetState: AsyncData, modifier: Modifier = Modifier, contentKey: (AsyncData) -> Any? = { it::class.java }, transitionSpec: AnimatedContentTransitionScope>.() -> ContentTransform = { ( fadeIn(animationSpec = tween(220, delayMillis = 90)) + scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)) ) .togetherWith(fadeOut(animationSpec = tween(90))) }, // default jetpack compose animated content animation spec loadingContent: @Composable BoxScope.() -> Unit = { SunoSpinner( modifier = Modifier .align(Alignment.Center) .padding(16.dp), ) }, errorContent: @Composable BoxScope.(E) -> Unit = { SunoError( modifier = Modifier .align(Alignment.Center) .padding(16.dp), ) }, readyContent: @Composable BoxScope.(T) -> Unit, ) { AnimatedContent( targetState = targetState, transitionSpec = transitionSpec, contentKey = contentKey, modifier = modifier, ) { state -> Box { when (state) { AsyncData.Loading -> loadingContent() is AsyncData.Error -> errorContent(state.error) is AsyncData.Ready -> readyContent(state.data) } } } } @Composable private fun AnimatedAsyncDataContent_Base_Preview( targetState: AsyncData, ) { SunoPreview { AnimatedAsyncDataContent( targetState = targetState, modifier = Modifier .fillMaxSize() .background(ExtendedTheme.colors.backgroundPrimary), ) { Text("This is a ready state") } } } @PreviewLightDark @Composable private fun AnimatedAsyncDataContent_Loading_Preview() { AnimatedAsyncDataContent_Base_Preview( targetState = AsyncData.Loading, ) } @PreviewLightDark @Composable private fun AnimatedAsyncDataContent_Error_Preview() { AnimatedAsyncDataContent_Base_Preview( targetState = AsyncData.Error(Unit), ) } @PreviewLightDark @Composable private fun AnimatedAsyncDataContent_Ready_Preview() { AnimatedAsyncDataContent_Base_Preview( targetState = AsyncData.Ready(Unit), ) }