feat: answer reveal, auth screens, challenges, onboarding, pairing, paywall, wheel, settings, components
This commit is contained in:
parent
b42544bafb
commit
125a24eb85
|
|
@ -25,7 +25,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
|
@ -141,7 +141,7 @@ private fun AnswerRevealContent(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
CloserHeartLoader(size = 32.dp)
|
||||||
Text("Loading reveal")
|
Text("Loading reveal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -451,7 +451,7 @@ private fun ReleasingKeyState() {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
CloserHeartLoader(size = 32.dp)
|
||||||
Text(
|
Text(
|
||||||
text = "Opening reveal…",
|
text = "Opening reveal…",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -157,11 +157,7 @@ fun ForgotPasswordScreen(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
color = AuthOnPrimary,
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
else Text("Send reset email", style = MaterialTheme.typography.labelLarge)
|
else Text("Send reset email", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -58,6 +57,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import app.closer.core.navigation.AppRoute
|
import app.closer.core.navigation.AppRoute
|
||||||
import app.closer.ui.components.BrandMessageRotator
|
import app.closer.ui.components.BrandMessageRotator
|
||||||
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreen(
|
fun LoginScreen(
|
||||||
|
|
@ -176,7 +176,7 @@ fun LoginScreen(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(color = AuthOnPrimary, strokeWidth = 2.dp)
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Sign in", style = MaterialTheme.typography.labelLarge)
|
else Text("Sign in", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -179,7 +179,7 @@ fun SignUpScreen(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(color = AuthOnPrimary, strokeWidth = 2.dp)
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Create account", style = MaterialTheme.typography.labelLarge)
|
else Text("Create account", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -241,7 +241,7 @@ fun ConnectionChallengesScreen(
|
||||||
@Composable
|
@Composable
|
||||||
private fun ChallengesLoadingScreen() {
|
private fun ChallengesLoadingScreen() {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
CloserHeartLoader()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,35 @@
|
||||||
package app.closer.ui.components
|
package app.closer.ui.components
|
||||||
|
|
||||||
import app.closer.ui.theme.CloserPalette
|
|
||||||
import app.closer.ui.theme.closerCardColor
|
import app.closer.ui.theme.closerCardColor
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import android.provider.Settings
|
||||||
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||||
|
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.graphics.vector.PathParser
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -31,8 +37,6 @@ fun LoadingState(
|
||||||
message: String = "Loading…",
|
message: String = "Loading…",
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val shimmer = closerSkeletonBrush()
|
|
||||||
|
|
||||||
CloserCard(
|
CloserCard(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
containerColor = closerCardColor(alpha = 0.8f)
|
containerColor = closerCardColor(alpha = 0.8f)
|
||||||
|
|
@ -41,23 +45,10 @@ fun LoadingState(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(CloserSpacing.Xxxl),
|
.padding(CloserSpacing.Xxxl),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
|
verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
|
||||||
) {
|
) {
|
||||||
SkeletonLine(
|
CloserHeartLoader()
|
||||||
brush = shimmer,
|
|
||||||
modifier = Modifier.fillMaxWidth(0.42f),
|
|
||||||
height = 18.dp
|
|
||||||
)
|
|
||||||
SkeletonLine(
|
|
||||||
brush = shimmer,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
height = 13.dp
|
|
||||||
)
|
|
||||||
SkeletonLine(
|
|
||||||
brush = shimmer,
|
|
||||||
modifier = Modifier.fillMaxWidth(0.72f),
|
|
||||||
height = 13.dp
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
text = message,
|
text = message,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
|
@ -73,39 +64,90 @@ fun LoadingState(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun closerSkeletonBrush(): Brush {
|
fun CloserHeartLoader(
|
||||||
val transition = rememberInfiniteTransition(label = "closerSkeleton")
|
modifier: Modifier = Modifier,
|
||||||
val shimmerOffset = transition.animateFloat(
|
size: Dp = 76.dp
|
||||||
initialValue = -320f,
|
) {
|
||||||
targetValue = 640f,
|
val context = LocalContext.current
|
||||||
|
val reducedMotion = remember {
|
||||||
|
Settings.Global.getFloat(
|
||||||
|
context.contentResolver,
|
||||||
|
Settings.Global.ANIMATOR_DURATION_SCALE,
|
||||||
|
1f
|
||||||
|
) == 0f
|
||||||
|
}
|
||||||
|
val transition = rememberInfiniteTransition(label = "closerHeartLoader")
|
||||||
|
val animatedFill = transition.animateFloat(
|
||||||
|
initialValue = 0.08f,
|
||||||
|
targetValue = 1f,
|
||||||
animationSpec = infiniteRepeatable(
|
animationSpec = infiniteRepeatable(
|
||||||
animation = tween(durationMillis = 1200, easing = LinearEasing),
|
animation = tween(durationMillis = 1500, easing = FastOutSlowInEasing),
|
||||||
repeatMode = RepeatMode.Restart
|
repeatMode = RepeatMode.Restart
|
||||||
),
|
),
|
||||||
label = "closerSkeletonOffset"
|
label = "closerHeartFill"
|
||||||
)
|
)
|
||||||
|
val animatedPulse = transition.animateFloat(
|
||||||
return Brush.linearGradient(
|
initialValue = 0.96f,
|
||||||
colors = listOf(
|
targetValue = 1.04f,
|
||||||
CloserPalette.PurpleMist.copy(alpha = 0.55f),
|
animationSpec = infiniteRepeatable(
|
||||||
CloserPalette.PurpleSoft.copy(alpha = 0.95f),
|
animation = tween(durationMillis = 900, easing = FastOutSlowInEasing),
|
||||||
CloserPalette.PinkMist.copy(alpha = 0.72f)
|
repeatMode = RepeatMode.Reverse
|
||||||
),
|
),
|
||||||
start = Offset(shimmerOffset.value, 0f),
|
label = "closerHeartPulse"
|
||||||
end = Offset(shimmerOffset.value + 280f, 0f)
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
val fillProgress = if (reducedMotion) 1f else animatedFill.value
|
||||||
private fun SkeletonLine(
|
val pulse = if (reducedMotion) 1f else animatedPulse.value
|
||||||
brush: Brush,
|
val shadowPath = remember {
|
||||||
modifier: Modifier = Modifier,
|
PathParser().parsePathString(
|
||||||
height: androidx.compose.ui.unit.Dp
|
"M54,89C48,82 25,65 20,50C15,35 23,22 37,22C45,22 51,26 54,33C57,26 63,22 71,22C85,22 93,35 88,50C83,65 60,82 54,89Z"
|
||||||
) {
|
).toPath()
|
||||||
Box(
|
}
|
||||||
|
val leftPath = remember {
|
||||||
|
PathParser().parsePathString(
|
||||||
|
"M54,85C49,79 27,62 22,48C17,35 24,24 37,24C45,24 51,28 54,35Z"
|
||||||
|
).toPath()
|
||||||
|
}
|
||||||
|
val rightPath = remember {
|
||||||
|
PathParser().parsePathString(
|
||||||
|
"M54,85C59,79 81,62 86,48C91,35 84,24 71,24C63,24 57,28 54,35Z"
|
||||||
|
).toPath()
|
||||||
|
}
|
||||||
|
val leftHighlight = remember {
|
||||||
|
PathParser().parsePathString(
|
||||||
|
"M27,42C28,32 34,27 42,27C48,27 52,30 54,35L54,41C47,36 37,36 27,42Z"
|
||||||
|
).toPath()
|
||||||
|
}
|
||||||
|
val rightHighlight = remember {
|
||||||
|
PathParser().parsePathString(
|
||||||
|
"M54,35C57,30 62,27 69,27C78,27 84,32 85,42C75,36 65,36 54,41Z"
|
||||||
|
).toPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.height(height)
|
.size(size)
|
||||||
.clip(RoundedCornerShape(CloserRadii.Pill))
|
.graphicsLayer {
|
||||||
.background(brush)
|
scaleX = pulse
|
||||||
)
|
scaleY = pulse
|
||||||
|
}
|
||||||
|
.clearAndSetSemantics {}
|
||||||
|
) {
|
||||||
|
val scaleX = this.size.width / 108f
|
||||||
|
val scaleY = this.size.height / 108f
|
||||||
|
|
||||||
|
withTransform({
|
||||||
|
scale(scaleX = scaleX, scaleY = scaleY, pivot = Offset.Zero)
|
||||||
|
}) {
|
||||||
|
drawPath(shadowPath, color = Color(0xFF24122F).copy(alpha = 0.10f))
|
||||||
|
drawPath(leftPath, color = Color(0xFFF7C8E4).copy(alpha = 0.22f))
|
||||||
|
drawPath(rightPath, color = Color(0xFFD9B8FF).copy(alpha = 0.22f))
|
||||||
|
clipRect(top = 108f * (1f - fillProgress), bottom = 108f) {
|
||||||
|
drawPath(leftPath, color = Color(0xFFF7C8E4))
|
||||||
|
drawPath(rightPath, color = Color(0xFFD9B8FF))
|
||||||
|
drawPath(leftHighlight, color = Color(0xFFFFF4FA).copy(alpha = 0.68f))
|
||||||
|
drawPath(rightHighlight, color = Color(0xFFF3E8FF).copy(alpha = 0.52f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
|
@ -370,9 +370,8 @@ fun DesireSyncScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (state.phase) {
|
when (state.phase) {
|
||||||
DesireSyncPhase.LOADING -> CircularProgressIndicator(
|
DesireSyncPhase.LOADING -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.Romantic
|
|
||||||
)
|
)
|
||||||
DesireSyncPhase.ERROR -> DSErrorScreen(
|
DesireSyncPhase.ERROR -> DSErrorScreen(
|
||||||
message = state.error ?: "Something didn't load. Go back and try again.",
|
message = state.error ?: "Something didn't load. Go back and try again.",
|
||||||
|
|
@ -553,7 +552,7 @@ private fun DSWaitingScreen(partnerName: String, onBack: () -> Unit, onAbandon:
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
CircularProgressIndicator(color = CloserPalette.Romantic, strokeWidth = 3.dp)
|
CloserHeartLoader(size = 48.dp)
|
||||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
|
|
@ -1020,9 +1019,8 @@ fun DSReplayScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (phase) {
|
when (phase) {
|
||||||
is DSReplayPhase.Loading -> CircularProgressIndicator(
|
is DSReplayPhase.Loading -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.Romantic
|
|
||||||
)
|
)
|
||||||
is DSReplayPhase.Error -> Column(
|
is DSReplayPhase.Error -> Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ 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.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
|
@ -130,10 +130,7 @@ fun WaitingForPartnerScreen(
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
state.isLoading -> {
|
state.isLoading -> {
|
||||||
CircularProgressIndicator(
|
CloserHeartLoader(size = 64.dp)
|
||||||
modifier = Modifier.size(48.dp),
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
text = "Loading...",
|
text = "Loading...",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
|
@ -414,9 +414,8 @@ fun HowWellScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (state.phase) {
|
when (state.phase) {
|
||||||
HowWellPhase.LOADING -> CircularProgressIndicator(
|
HowWellPhase.LOADING -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
HowWellPhase.ERROR -> HowWellErrorScreen(
|
HowWellPhase.ERROR -> HowWellErrorScreen(
|
||||||
message = state.error ?: "Something didn't load. Go back and try again.",
|
message = state.error ?: "Something didn't load. Go back and try again.",
|
||||||
|
|
@ -622,7 +621,7 @@ private fun HowWellWaitingScreen(amSubject: Boolean, partnerName: String, onBack
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
CloserHeartLoader(size = 48.dp)
|
||||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
|
|
@ -1202,9 +1201,8 @@ fun HowWellReplayScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (phase) {
|
when (phase) {
|
||||||
is HWReplayPhase.Loading -> CircularProgressIndicator(
|
is HWReplayPhase.Loading -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
is HWReplayPhase.Error -> Column(
|
is HWReplayPhase.Error -> Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.FilterChipDefaults
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -231,7 +231,7 @@ fun MemoryLaneScreen(
|
||||||
) {
|
) {
|
||||||
when (state.phase) {
|
when (state.phase) {
|
||||||
MemoryLanePhase.LOADING -> Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
MemoryLanePhase.LOADING -> Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
CloserHeartLoader()
|
||||||
}
|
}
|
||||||
MemoryLanePhase.LIST -> CapsuleListScreen(
|
MemoryLanePhase.LIST -> CapsuleListScreen(
|
||||||
capsules = state.capsules,
|
capsules = state.capsules,
|
||||||
|
|
@ -494,7 +494,7 @@ private fun CapsuleCreateScreen(
|
||||||
shape = RoundedCornerShape(18.dp),
|
shape = RoundedCornerShape(18.dp),
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.PurpleDeep)
|
colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.PurpleDeep)
|
||||||
) {
|
) {
|
||||||
if (state.isSaving) CircularProgressIndicator(color = Color.White, modifier = Modifier.size(20.dp), strokeWidth = 2.dp)
|
if (state.isSaving) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Seal the capsule 📦", color = Color.White, style = MaterialTheme.typography.labelLarge)
|
else Text("Seal the capsule 📦", color = Color.White, style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.PhotoLibrary
|
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -255,7 +255,7 @@ private fun NameStep(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(color = AuthOnPrimary, strokeWidth = 2.dp)
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,7 +312,7 @@ private fun SexStep(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(color = AuthOnPrimary, strokeWidth = 2.dp)
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -440,7 +440,7 @@ private fun PhotoStep(
|
||||||
contentColor = AuthOnPrimary
|
contentColor = AuthOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(color = AuthOnPrimary, strokeWidth = 2.dp)
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ 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.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -55,6 +54,7 @@ import app.closer.ui.auth.AuthOnPrimary
|
||||||
import app.closer.ui.auth.AuthPrimary
|
import app.closer.ui.auth.AuthPrimary
|
||||||
import app.closer.ui.auth.AuthPrimaryDeep
|
import app.closer.ui.auth.AuthPrimaryDeep
|
||||||
import app.closer.ui.components.BrandMessageRotator
|
import app.closer.ui.components.BrandMessageRotator
|
||||||
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import app.closer.ui.theme.CloserPalette
|
import app.closer.ui.theme.CloserPalette
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -85,11 +85,7 @@ fun OnboardingScreen(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(
|
CloserHeartLoader()
|
||||||
modifier = Modifier.size(36.dp),
|
|
||||||
color = AuthPrimary,
|
|
||||||
strokeWidth = 3.dp
|
|
||||||
)
|
|
||||||
BrandMessageRotator(color = AuthMuted)
|
BrandMessageRotator(color = AuthMuted)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.filled.TrendingUp
|
import androidx.compose.material.icons.automirrored.filled.TrendingUp
|
||||||
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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -98,7 +98,7 @@ fun YourProgressScreen(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator()
|
CloserHeartLoader()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.error != null -> {
|
state.error != null -> {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -166,11 +166,7 @@ fun AcceptInviteScreen(
|
||||||
contentColor = SettingsOnPrimary
|
contentColor = SettingsOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) CircularProgressIndicator(
|
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
color = SettingsOnPrimary,
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
else Text("Continue", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -125,7 +125,7 @@ fun CreateInviteScreen(
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
Spacer(Modifier.height(160.dp))
|
Spacer(Modifier.height(160.dp))
|
||||||
CircularProgressIndicator(modifier = Modifier.size(40.dp))
|
CloserHeartLoader(size = 64.dp)
|
||||||
} else if (state.inviteCode != null) {
|
} else if (state.inviteCode != null) {
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Lock
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -38,6 +37,7 @@ import app.closer.data.remote.FirestoreCoupleDataSource
|
||||||
import app.closer.domain.repository.AuthRepository
|
import app.closer.domain.repository.AuthRepository
|
||||||
import app.closer.domain.repository.CoupleRepository
|
import app.closer.domain.repository.CoupleRepository
|
||||||
import app.closer.ui.components.BrandMessageRotator
|
import app.closer.ui.components.BrandMessageRotator
|
||||||
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import app.closer.ui.components.StatusGlyph
|
import app.closer.ui.components.StatusGlyph
|
||||||
import app.closer.ui.settings.SettingsBackgroundBrush
|
import app.closer.ui.settings.SettingsBackgroundBrush
|
||||||
import app.closer.ui.settings.SettingsInk
|
import app.closer.ui.settings.SettingsInk
|
||||||
|
|
@ -181,7 +181,7 @@ fun EncryptionUpgradeScreen(
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
CircularProgressIndicator(color = SettingsPrimary)
|
CloserHeartLoader()
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -107,7 +107,7 @@ fun InviteConfirmScreen(
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
Spacer(Modifier.height(160.dp))
|
Spacer(Modifier.height(160.dp))
|
||||||
CircularProgressIndicator(modifier = Modifier.size(40.dp))
|
CloserHeartLoader(size = 64.dp)
|
||||||
} else {
|
} else {
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(24.dp))
|
||||||
|
|
||||||
|
|
@ -156,11 +156,7 @@ fun InviteConfirmScreen(
|
||||||
contentColor = SettingsOnPrimary
|
contentColor = SettingsOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isConfirming) CircularProgressIndicator(
|
if (state.isConfirming) CloserHeartLoader(size = 22.dp)
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
color = SettingsOnPrimary,
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
else Text("Pair up", style = MaterialTheme.typography.labelLarge)
|
else Text("Pair up", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Lock
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
|
|
@ -141,11 +141,7 @@ fun RecoveryScreen(
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
CircularProgressIndicator(
|
CloserHeartLoader(size = 22.dp)
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
color = SettingsOnPrimary,
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Text("Unlock answers", style = MaterialTheme.typography.labelLarge)
|
Text("Unlock answers", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
|
|
@ -133,10 +133,7 @@ fun PaywallScreen(
|
||||||
BenefitsCard()
|
BenefitsCard()
|
||||||
|
|
||||||
if (uiState.purchaseState is BillingState.Loading) {
|
if (uiState.purchaseState is BillingState.Loading) {
|
||||||
CircularProgressIndicator(
|
CloserHeartLoader(size = 40.dp)
|
||||||
modifier = Modifier.size(28.dp),
|
|
||||||
color = CloserPalette.PurpleRich
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionButtons(
|
ActionButtons(
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -401,7 +401,7 @@ private fun CategoryLoadingCard() {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
CloserHeartLoader(size = 32.dp)
|
||||||
Text(
|
Text(
|
||||||
text = "Loading prompts",
|
text = "Loading prompts",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -424,7 +424,7 @@ private fun LoadingPackCard() {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
CloserHeartLoader(size = 32.dp)
|
||||||
Text(
|
Text(
|
||||||
text = "Loading question packs",
|
text = "Loading question packs",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -251,7 +251,7 @@ fun DeleteAccountScreen(
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isReauthing) {
|
if (state.isReauthing) {
|
||||||
CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.White, strokeWidth = 2.dp)
|
CloserHeartLoader(size = 18.dp)
|
||||||
} else {
|
} else {
|
||||||
Text("Confirm", color = Color.White)
|
Text("Confirm", color = Color.White)
|
||||||
}
|
}
|
||||||
|
|
@ -328,7 +328,7 @@ fun DeleteAccountScreen(
|
||||||
) {
|
) {
|
||||||
if (state.isDeleting) {
|
if (state.isDeleting) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
CircularProgressIndicator(modifier = Modifier.size(20.dp), color = Color.White, strokeWidth = 2.dp)
|
CloserHeartLoader(size = 20.dp)
|
||||||
Text("Deleting…", color = Color.White)
|
Text("Deleting…", color = Color.White)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.PhotoLibrary
|
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -175,7 +175,7 @@ fun EditProfileContent(
|
||||||
) {
|
) {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
Spacer(Modifier.height(64.dp))
|
Spacer(Modifier.height(64.dp))
|
||||||
CircularProgressIndicator(color = SettingsPrimary)
|
CloserHeartLoader()
|
||||||
} else {
|
} else {
|
||||||
val imageUri = state.photoUri ?: state.photoUrl.takeIf { it.isNotBlank() }
|
val imageUri = state.photoUri ?: state.photoUrl.takeIf { it.isNotBlank() }
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -356,7 +356,7 @@ fun EditProfileContent(
|
||||||
contentColor = SettingsOnPrimary
|
contentColor = SettingsOnPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isSaving) CircularProgressIndicator(color = SettingsOnPrimary, strokeWidth = 2.dp)
|
if (state.isSaving) CloserHeartLoader(size = 22.dp)
|
||||||
else Text("Save changes", style = MaterialTheme.typography.labelLarge)
|
else Text("Save changes", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -190,7 +190,7 @@ fun RelationshipSettingsScreen(
|
||||||
) {
|
) {
|
||||||
if (state.isLeaving) {
|
if (state.isLeaving) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
CircularProgressIndicator(modifier = Modifier.size(20.dp), color = Color.White, strokeWidth = 2.dp)
|
CloserHeartLoader(size = 20.dp)
|
||||||
Text("Leaving…", color = Color.White)
|
Text("Leaving…", color = Color.White)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -226,7 +226,7 @@ fun SettingsScreen(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator()
|
CloserHeartLoader()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var showBaselineOutcomeDialog by remember { mutableStateOf(false) }
|
var showBaselineOutcomeDialog by remember { mutableStateOf(false) }
|
||||||
|
|
@ -499,11 +499,7 @@ fun SettingsScreen(
|
||||||
contentColor = SettingsDanger
|
contentColor = SettingsDanger
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (state.isSigningOut) CircularProgressIndicator(
|
if (state.isSigningOut) CloserHeartLoader(size = 22.dp)
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
color = SettingsDanger,
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
else Text("Sign out", style = MaterialTheme.typography.labelLarge)
|
else Text("Sign out", style = MaterialTheme.typography.labelLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
|
@ -417,9 +417,8 @@ fun ThisOrThatScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (state.phase) {
|
when (state.phase) {
|
||||||
TotPhase.LOADING -> CircularProgressIndicator(
|
TotPhase.LOADING -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
TotPhase.ERROR -> ErrorState(
|
TotPhase.ERROR -> ErrorState(
|
||||||
message = state.error ?: "Something didn't load. Go back and try again.",
|
message = state.error ?: "Something didn't load. Go back and try again.",
|
||||||
|
|
@ -447,9 +446,8 @@ fun ThisOrThatScreen(
|
||||||
TotPhase.PLAYING -> {
|
TotPhase.PLAYING -> {
|
||||||
val question = state.questions.getOrNull(state.currentIndex)
|
val question = state.questions.getOrNull(state.currentIndex)
|
||||||
if (question == null) {
|
if (question == null) {
|
||||||
CircularProgressIndicator(
|
CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val config = question.answerConfig as? ThisOrThatAnswerConfigImpl
|
val config = question.answerConfig as? ThisOrThatAnswerConfigImpl
|
||||||
|
|
@ -909,7 +907,7 @@ private fun WaitingForRevealScreen(
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
CloserHeartLoader(size = 48.dp)
|
||||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||||
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
@ -1272,9 +1270,8 @@ fun ThisOrThatReplayScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (phase) {
|
when (phase) {
|
||||||
is TotReplayPhase.Loading -> CircularProgressIndicator(
|
is TotReplayPhase.Loading -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
is TotReplayPhase.Error -> Column(
|
is TotReplayPhase.Error -> Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
|
@ -104,7 +104,7 @@ private fun CategoryPickerContent(
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
CloserHeartLoader(size = 28.dp)
|
||||||
Text(
|
Text(
|
||||||
"Loading categories…",
|
"Loading categories…",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Lock
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -262,7 +262,7 @@ private fun SpinWheelContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.isLoading -> CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
state.isLoading -> CloserHeartLoader()
|
||||||
else -> {
|
else -> {
|
||||||
Text(
|
Text(
|
||||||
text = "Spin to discover a random category and 10 questions",
|
text = "Spin to discover a random category and 10 questions",
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
|
@ -197,9 +197,8 @@ fun WheelCompleteScreen(
|
||||||
.background(closerBackgroundBrush())
|
.background(closerBackgroundBrush())
|
||||||
) {
|
) {
|
||||||
when (state.phase) {
|
when (state.phase) {
|
||||||
WheelRevealPhase.LOADING -> CircularProgressIndicator(
|
WheelRevealPhase.LOADING -> CloserHeartLoader(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center)
|
||||||
color = CloserPalette.PurpleDeep
|
|
||||||
)
|
)
|
||||||
WheelRevealPhase.WAITING -> WheelWaitingContent(
|
WheelRevealPhase.WAITING -> WheelWaitingContent(
|
||||||
partnerName = state.partnerName,
|
partnerName = state.partnerName,
|
||||||
|
|
@ -254,7 +253,7 @@ private fun WheelWaitingContent(
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
CloserHeartLoader(size = 48.dp)
|
||||||
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.CheckboxDefaults
|
import androidx.compose.material3.CheckboxDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import app.closer.ui.components.CloserHeartLoader
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
|
@ -114,7 +114,7 @@ private fun WheelSessionContent(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFF56306F))
|
CloserHeartLoader()
|
||||||
}
|
}
|
||||||
return@Column
|
return@Column
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ struct LoadingView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: CloserSpacing.lg) {
|
VStack(spacing: CloserSpacing.lg) {
|
||||||
ProgressView()
|
CloserHeartLoader()
|
||||||
.tint(.closerPrimary)
|
|
||||||
.scaleEffect(1.2)
|
|
||||||
Text(message)
|
Text(message)
|
||||||
.font(CloserFont.callout)
|
.font(CloserFont.callout)
|
||||||
.foregroundColor(.closerTextSecondary)
|
.foregroundColor(.closerTextSecondary)
|
||||||
|
|
@ -18,6 +16,168 @@ struct LoadingView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CloserHeartLoader: View {
|
||||||
|
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
||||||
|
@State private var fillProgress: CGFloat = 0.08
|
||||||
|
@State private var isPulsing = false
|
||||||
|
|
||||||
|
var size: CGFloat = 76
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
CloserHeartShape()
|
||||||
|
.fill(
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Color(hex: "F7C8E4").opacity(0.22),
|
||||||
|
Color(hex: "D9B8FF").opacity(0.22)
|
||||||
|
],
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Color(hex: "F7C8E4")
|
||||||
|
Color(hex: "D9B8FF")
|
||||||
|
}
|
||||||
|
.mask(CloserHeartShape())
|
||||||
|
.mask(alignment: .bottom) {
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: size * (reduceMotion ? 1 : fillProgress))
|
||||||
|
}
|
||||||
|
|
||||||
|
CloserHeartHighlightShape(side: .left)
|
||||||
|
.fill(Color(hex: "FFF4FA").opacity(reduceMotion ? 0.68 : 0.68 * fillProgress))
|
||||||
|
|
||||||
|
CloserHeartHighlightShape(side: .right)
|
||||||
|
.fill(Color(hex: "F3E8FF").opacity(reduceMotion ? 0.52 : 0.52 * fillProgress))
|
||||||
|
}
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.scaleEffect(reduceMotion ? 1 : (isPulsing ? 1.04 : 0.96))
|
||||||
|
.accessibilityHidden(true)
|
||||||
|
.onAppear {
|
||||||
|
guard !reduceMotion else {
|
||||||
|
fillProgress = 1
|
||||||
|
isPulsing = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
withAnimation(.easeInOut(duration: 1.5).repeatForever(autoreverses: false)) {
|
||||||
|
fillProgress = 1
|
||||||
|
}
|
||||||
|
withAnimation(.easeInOut(duration: 0.9).repeatForever(autoreverses: true)) {
|
||||||
|
isPulsing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: reduceMotion) { _, newValue in
|
||||||
|
if newValue {
|
||||||
|
fillProgress = 1
|
||||||
|
isPulsing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CloserHeartShape: Shape {
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
path.move(to: point(54, 85, in: rect))
|
||||||
|
path.addCurve(
|
||||||
|
to: point(22, 48, in: rect),
|
||||||
|
control1: point(49, 79, in: rect),
|
||||||
|
control2: point(27, 62, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(37, 24, in: rect),
|
||||||
|
control1: point(17, 35, in: rect),
|
||||||
|
control2: point(24, 24, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(54, 35, in: rect),
|
||||||
|
control1: point(45, 24, in: rect),
|
||||||
|
control2: point(51, 28, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(71, 24, in: rect),
|
||||||
|
control1: point(57, 28, in: rect),
|
||||||
|
control2: point(63, 24, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(86, 48, in: rect),
|
||||||
|
control1: point(84, 24, in: rect),
|
||||||
|
control2: point(91, 35, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(54, 85, in: rect),
|
||||||
|
control1: point(81, 62, in: rect),
|
||||||
|
control2: point(59, 79, in: rect)
|
||||||
|
)
|
||||||
|
path.closeSubpath()
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CloserHeartHighlightShape: Shape {
|
||||||
|
enum Side {
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
|
let side: Side
|
||||||
|
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
switch side {
|
||||||
|
case .left:
|
||||||
|
path.move(to: point(27, 42, in: rect))
|
||||||
|
path.addCurve(
|
||||||
|
to: point(42, 27, in: rect),
|
||||||
|
control1: point(28, 32, in: rect),
|
||||||
|
control2: point(34, 27, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(54, 35, in: rect),
|
||||||
|
control1: point(48, 27, in: rect),
|
||||||
|
control2: point(52, 30, in: rect)
|
||||||
|
)
|
||||||
|
path.addLine(to: point(54, 41, in: rect))
|
||||||
|
path.addCurve(
|
||||||
|
to: point(27, 42, in: rect),
|
||||||
|
control1: point(47, 36, in: rect),
|
||||||
|
control2: point(37, 36, in: rect)
|
||||||
|
)
|
||||||
|
path.closeSubpath()
|
||||||
|
case .right:
|
||||||
|
path.move(to: point(54, 35, in: rect))
|
||||||
|
path.addCurve(
|
||||||
|
to: point(69, 27, in: rect),
|
||||||
|
control1: point(57, 30, in: rect),
|
||||||
|
control2: point(62, 27, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(85, 42, in: rect),
|
||||||
|
control1: point(78, 27, in: rect),
|
||||||
|
control2: point(84, 32, in: rect)
|
||||||
|
)
|
||||||
|
path.addCurve(
|
||||||
|
to: point(54, 41, in: rect),
|
||||||
|
control1: point(75, 36, in: rect),
|
||||||
|
control2: point(65, 36, in: rect)
|
||||||
|
)
|
||||||
|
path.closeSubpath()
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func point(_ x: CGFloat, _ y: CGFloat, in rect: CGRect) -> CGPoint {
|
||||||
|
CGPoint(
|
||||||
|
x: rect.minX + rect.width * (x / 108),
|
||||||
|
y: rect.minY + rect.height * (y / 108)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Error View
|
// MARK: - Error View
|
||||||
|
|
||||||
struct ErrorView: View {
|
struct ErrorView: View {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue