fix(home,onboarding): correct navigation and state handling
This commit is contained in:
parent
db177bc792
commit
342c3276a0
|
|
@ -1,5 +1,12 @@
|
||||||
package com.couplesconnect.app.ui.home
|
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.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
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.safeDrawingPadding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
|
@ -31,6 +41,7 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -102,10 +113,6 @@ private fun HomeContent(
|
||||||
streakCount = state.streakCount
|
streakCount = state.streakCount
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!state.isPaired && !state.isLoading) {
|
|
||||||
InvitePartnerCard(onInvite = onInvite)
|
|
||||||
}
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
state.isLoading -> LoadingHomeCard()
|
state.isLoading -> LoadingHomeCard()
|
||||||
state.error != null -> ErrorHomeCard(message = state.error, onRefresh = onRefresh)
|
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
|
@Composable
|
||||||
private fun InvitePartnerCard(onInvite: () -> Unit) {
|
private fun PulsingInviteFab(
|
||||||
Card(
|
onClick: () -> Unit,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier: Modifier = Modifier
|
||||||
shape = RoundedCornerShape(26.dp),
|
) {
|
||||||
colors = CardDefaults.cardColors(containerColor = Color(0xFFFFF4F0)),
|
val infiniteTransition = rememberInfiniteTransition(label = "fab_pulse")
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
|
|
||||||
|
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(
|
// Expanding ring 1
|
||||||
modifier = Modifier.padding(18.dp),
|
Box(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
modifier = Modifier
|
||||||
verticalAlignment = Alignment.CenterVertically
|
.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(
|
Icon(
|
||||||
Icons.Filled.Favorite,
|
Icons.Filled.Add,
|
||||||
contentDescription = null,
|
contentDescription = "Invite partner",
|
||||||
tint = Color(0xFFE07A5F),
|
|
||||||
modifier = Modifier.size(28.dp)
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,7 @@ class OnboardingViewModel @Inject constructor(
|
||||||
val user = runCatching { userRepository.getUser(authState.userId) }.getOrNull()
|
val user = runCatching { userRepository.getUser(authState.userId) }.getOrNull()
|
||||||
val destination = when {
|
val destination = when {
|
||||||
user == null || user.displayName.isBlank() -> "create_profile"
|
user == null || user.displayName.isBlank() -> "create_profile"
|
||||||
user.coupleId != null -> "home"
|
else -> "home"
|
||||||
else -> "create_invite"
|
|
||||||
}
|
}
|
||||||
_uiState.update { it.copy(isCheckingAuth = false, navigateTo = destination) }
|
_uiState.update { it.copy(isCheckingAuth = false, navigateTo = destination) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue