diff --git a/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/cocktail/component/CreditText.kt b/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/cocktail/component/CreditText.kt index 00abe9d..50f9380 100644 --- a/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/cocktail/component/CreditText.kt +++ b/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/cocktail/component/CreditText.kt @@ -1,19 +1,19 @@ package com.aslansari.spiritvisor.cocktail.component +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateOffsetAsState +import androidx.compose.animation.core.animateSizeAsState import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.icons.Icons -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.SpanStyle @@ -21,9 +21,16 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import com.aslansari.spiritvisor.theme.icon.HeartSharp +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.map @Composable -fun CreditText(modifier: Modifier = Modifier) { +fun CreditText( + modifier: Modifier = Modifier, + onLoveSurge: () -> Unit = {}, +) { Row( modifier = modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically, @@ -41,12 +48,44 @@ fun CreditText(modifier: Modifier = Modifier) { onClick = { uriHandler.openUri("https://bento.me/aslansari") }, interactionSource = remember { MutableInteractionSource() }, indication = null, - ) , + ), text = creditText, style = MaterialTheme.typography.body2.copy(color = Color(0xFF697077)) ) Spacer(Modifier.size(2.dp)) + var anim by remember { mutableStateOf(false) } + LaunchedEffect(anim) { + delay(200) + anim = false + } + val heartIconSize by animateDpAsState(targetValue = if (anim) 48.dp else 16.dp) + val heardIconOffset by animateDpAsState(targetValue = if (anim) 0.dp.unaryMinus() else 0.dp) + + val lotsOfLoveCounter = remember { mutableStateOf(0) } + LaunchedEffect(Unit) { + snapshotFlow { lotsOfLoveCounter.value } + .map { + if (it > 3) { onLoveSurge() } + it + } + .debounce(1000) + .collect { + lotsOfLoveCounter.value = 0 + } + } + Icon( + modifier = Modifier + .offset(y = heardIconOffset) + .size(heartIconSize) + .clickable( + onClick = { + anim = true + lotsOfLoveCounter.value++ + }, + interactionSource = remember { MutableInteractionSource() }, + indication = null, + ), imageVector = Icons.HeartSharp, contentDescription = null, tint = Color(0xFFDA1E28) diff --git a/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/home/HomeScreen.kt index a0fa65f..b5e5715 100644 --- a/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/aslansari/spiritvisor/home/HomeScreen.kt @@ -2,25 +2,27 @@ package com.aslansari.spiritvisor.home +import androidx.compose.animation.core.* import androidx.compose.foundation.layout.* -import androidx.compose.material.Button -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material.* +import androidx.compose.material.icons.Icons import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import androidx.compose.ui.zIndex import androidx.lifecycle.viewmodel.compose.viewModel import com.aslansari.spiritvisor.cocktail.component.CreditText +import com.aslansari.spiritvisor.theme.icon.HeartSharp +import kotlinx.coroutines.delay @Composable internal fun HomeRoute( @@ -53,6 +55,8 @@ internal fun HomeScreen( } val windowSizeClass = calculateWindowSizeClass() Box(modifier = modifier.fillMaxSize()) { + var showLove by remember { mutableStateOf(false) } + HeartAnimation(showLove = showLove) { showLove = false } Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -82,7 +86,10 @@ internal fun HomeScreen( } } } - CreditText(modifier = Modifier.align(Alignment.BottomCenter)) + CreditText( + modifier = Modifier.align(Alignment.BottomCenter), + onLoveSurge = { showLove = true } + ) } } @@ -105,3 +112,29 @@ private fun RowScope.FlavorCategoryButton( ) } } + +@Composable +internal fun BoxScope.HeartAnimation( + modifier: Modifier = Modifier, + showLove: Boolean = false, + onFinished: () -> Unit = {}, +) { + val anim by rememberUpdatedState(showLove) + LaunchedEffect(anim) { + delay(1000) + onFinished() + } + val heartIconSize by animateDpAsState(targetValue = if (anim) 400.dp else 32.dp, animationSpec = tween(800)) + val animateAlpha by animateFloatAsState(targetValue = if (anim) 1f else 0f, animationSpec = tween(600)) + if (anim) { + Icon( + modifier = modifier.zIndex(1f) + .align(Alignment.Center) + .alpha(animateAlpha) + .size(heartIconSize), + imageVector = Icons.HeartSharp, + contentDescription = null, + tint = Color(0xFFDA1E28) + ) + } +} \ No newline at end of file