diff --git a/app/src/main/java/com/couplesconnect/app/ui/home/HomeScreen.kt b/app/src/main/java/com/couplesconnect/app/ui/home/HomeScreen.kt index eca826ff..6f3cbb2e 100644 --- a/app/src/main/java/com/couplesconnect/app/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/couplesconnect/app/ui/home/HomeScreen.kt @@ -1,5 +1,12 @@ package com.couplesconnect.app.ui.home +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -12,15 +19,18 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Favorite import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton @@ -31,6 +41,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color @@ -102,10 +113,6 @@ private fun HomeContent( streakCount = state.streakCount ) - if (!state.isPaired && !state.isLoading) { - InvitePartnerCard(onInvite = onInvite) - } - when { state.isLoading -> LoadingHomeCard() state.error != null -> ErrorHomeCard(message = state.error, onRefresh = onRefresh) @@ -133,6 +140,16 @@ private fun HomeContent( } } } + + if (!state.isPaired && !state.isLoading) { + PulsingInviteFab( + onClick = onInvite, + modifier = Modifier + .align(Alignment.BottomEnd) + .navigationBarsPadding() + .padding(24.dp) + ) + } } } @@ -169,44 +186,95 @@ private fun HomeHeader( } @Composable -private fun InvitePartnerCard(onInvite: () -> Unit) { - Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(26.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFFFFF4F0)), - elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) +private fun PulsingInviteFab( + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + val infiniteTransition = rememberInfiniteTransition(label = "fab_pulse") + + val ring1Scale by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 2.2f, + animationSpec = infiniteRepeatable( + animation = tween(1400, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "ring1" + ) + val ring1Alpha by infiniteTransition.animateFloat( + initialValue = 0.6f, + targetValue = 0f, + animationSpec = infiniteRepeatable( + animation = tween(1400, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "ring1a" + ) + val ring2Scale by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 2.2f, + animationSpec = infiniteRepeatable( + animation = tween(1400, delayMillis = 500, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "ring2" + ) + val ring2Alpha by infiniteTransition.animateFloat( + initialValue = 0.6f, + targetValue = 0f, + animationSpec = infiniteRepeatable( + animation = tween(1400, delayMillis = 500, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "ring2a" + ) + val fabScale by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 1.08f, + animationSpec = infiniteRepeatable( + animation = tween(700, easing = FastOutSlowInEasing), + repeatMode = RepeatMode.Reverse + ), + label = "fabscale" + ) + + Box( + modifier = modifier.size(72.dp), + contentAlignment = Alignment.Center ) { - Row( - modifier = Modifier.padding(18.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically + // Expanding ring 1 + Box( + modifier = Modifier + .size(56.dp) + .scale(ring1Scale) + .background( + color = Color(0xFFE07A5F).copy(alpha = ring1Alpha), + shape = CircleShape + ) + ) + // Expanding ring 2 (offset start) + Box( + modifier = Modifier + .size(56.dp) + .scale(ring2Scale) + .background( + color = Color(0xFFE07A5F).copy(alpha = ring2Alpha), + shape = CircleShape + ) + ) + // FAB + FloatingActionButton( + onClick = onClick, + modifier = Modifier.size(56.dp).scale(fabScale), + containerColor = Color(0xFFE07A5F), + contentColor = Color.White, + shape = CircleShape ) { Icon( - Icons.Filled.Favorite, - contentDescription = null, - tint = Color(0xFFE07A5F), + Icons.Filled.Add, + contentDescription = "Invite partner", modifier = Modifier.size(28.dp) ) - Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { - Text( - text = "Invite your partner", - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - color = Color(0xFF27211F) - ) - Text( - text = "Share a code to connect and start answering together.", - style = MaterialTheme.typography.bodySmall, - color = Color(0xFF4E4642) - ) - } - Button( - onClick = onInvite, - shape = RoundedCornerShape(14.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE07A5F)) - ) { - Text("Invite", color = Color.White) - } } } } diff --git a/app/src/main/java/com/couplesconnect/app/ui/onboarding/OnboardingViewModel.kt b/app/src/main/java/com/couplesconnect/app/ui/onboarding/OnboardingViewModel.kt index 21f2e70c..6af15275 100644 --- a/app/src/main/java/com/couplesconnect/app/ui/onboarding/OnboardingViewModel.kt +++ b/app/src/main/java/com/couplesconnect/app/ui/onboarding/OnboardingViewModel.kt @@ -37,8 +37,7 @@ class OnboardingViewModel @Inject constructor( val user = runCatching { userRepository.getUser(authState.userId) }.getOrNull() val destination = when { user == null || user.displayName.isBlank() -> "create_profile" - user.coupleId != null -> "home" - else -> "create_invite" + else -> "home" } _uiState.update { it.copy(isCheckingAuth = false, navigateTo = destination) } }