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.Card
|
||||
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.OutlinedButton
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
|
|
@ -141,7 +141,7 @@ private fun AnswerRevealContent(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
||||
CloserHeartLoader(size = 32.dp)
|
||||
Text("Loading reveal")
|
||||
}
|
||||
}
|
||||
|
|
@ -451,7 +451,7 @@ private fun ReleasingKeyState() {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
||||
CloserHeartLoader(size = 32.dp)
|
||||
Text(
|
||||
text = "Opening reveal…",
|
||||
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.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -157,11 +157,7 @@ fun ForgotPasswordScreen(
|
|||
contentColor = AuthOnPrimary
|
||||
)
|
||||
) {
|
||||
if (state.isLoading) CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = AuthOnPrimary,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||
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.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -58,6 +57,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import app.closer.core.navigation.AppRoute
|
||||
import app.closer.ui.components.BrandMessageRotator
|
||||
import app.closer.ui.components.CloserHeartLoader
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(
|
||||
|
|
@ -176,7 +176,7 @@ fun LoginScreen(
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.filled.Visibility
|
|||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -179,7 +179,7 @@ fun SignUpScreen(
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -241,7 +241,7 @@ fun ConnectionChallengesScreen(
|
|||
@Composable
|
||||
private fun ChallengesLoadingScreen() {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
||||
CloserHeartLoader()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,35 @@
|
|||
package app.closer.ui.components
|
||||
|
||||
import app.closer.ui.theme.CloserPalette
|
||||
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.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.Canvas
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
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.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
|
|
@ -31,8 +37,6 @@ fun LoadingState(
|
|||
message: String = "Loading…",
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val shimmer = closerSkeletonBrush()
|
||||
|
||||
CloserCard(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
containerColor = closerCardColor(alpha = 0.8f)
|
||||
|
|
@ -41,23 +45,10 @@ fun LoadingState(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(CloserSpacing.Xxxl),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
|
||||
) {
|
||||
SkeletonLine(
|
||||
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
|
||||
)
|
||||
CloserHeartLoader()
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
|
|
@ -73,39 +64,90 @@ fun LoadingState(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun closerSkeletonBrush(): Brush {
|
||||
val transition = rememberInfiniteTransition(label = "closerSkeleton")
|
||||
val shimmerOffset = transition.animateFloat(
|
||||
initialValue = -320f,
|
||||
targetValue = 640f,
|
||||
fun CloserHeartLoader(
|
||||
modifier: Modifier = Modifier,
|
||||
size: Dp = 76.dp
|
||||
) {
|
||||
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(
|
||||
animation = tween(durationMillis = 1200, easing = LinearEasing),
|
||||
animation = tween(durationMillis = 1500, easing = FastOutSlowInEasing),
|
||||
repeatMode = RepeatMode.Restart
|
||||
),
|
||||
label = "closerSkeletonOffset"
|
||||
label = "closerHeartFill"
|
||||
)
|
||||
|
||||
return Brush.linearGradient(
|
||||
colors = listOf(
|
||||
CloserPalette.PurpleMist.copy(alpha = 0.55f),
|
||||
CloserPalette.PurpleSoft.copy(alpha = 0.95f),
|
||||
CloserPalette.PinkMist.copy(alpha = 0.72f)
|
||||
val animatedPulse = transition.animateFloat(
|
||||
initialValue = 0.96f,
|
||||
targetValue = 1.04f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = 900, easing = FastOutSlowInEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
),
|
||||
start = Offset(shimmerOffset.value, 0f),
|
||||
end = Offset(shimmerOffset.value + 280f, 0f)
|
||||
label = "closerHeartPulse"
|
||||
)
|
||||
|
||||
val fillProgress = if (reducedMotion) 1f else animatedFill.value
|
||||
val pulse = if (reducedMotion) 1f else animatedPulse.value
|
||||
val shadowPath = remember {
|
||||
PathParser().parsePathString(
|
||||
"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()
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SkeletonLine(
|
||||
brush: Brush,
|
||||
modifier: Modifier = Modifier,
|
||||
height: androidx.compose.ui.unit.Dp
|
||||
) {
|
||||
Box(
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.height(height)
|
||||
.clip(RoundedCornerShape(CloserRadii.Pill))
|
||||
.background(brush)
|
||||
)
|
||||
.size(size)
|
||||
.graphicsLayer {
|
||||
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.Card
|
||||
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.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -370,9 +370,8 @@ fun DesireSyncScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (state.phase) {
|
||||
DesireSyncPhase.LOADING -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.Romantic
|
||||
DesireSyncPhase.LOADING -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
DesireSyncPhase.ERROR -> DSErrorScreen(
|
||||
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
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
CircularProgressIndicator(color = CloserPalette.Romantic, strokeWidth = 3.dp)
|
||||
CloserHeartLoader(size = 48.dp)
|
||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||
Spacer(Modifier.weight(1f))
|
||||
OutlinedButton(
|
||||
|
|
@ -1020,9 +1019,8 @@ fun DSReplayScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (phase) {
|
||||
is DSReplayPhase.Loading -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.Romantic
|
||||
is DSReplayPhase.Loading -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
is DSReplayPhase.Error -> Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
|
|
@ -130,10 +130,7 @@ fun WaitingForPartnerScreen(
|
|||
) {
|
||||
when {
|
||||
state.isLoading -> {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(48.dp),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
CloserHeartLoader(size = 64.dp)
|
||||
Text(
|
||||
text = "Loading...",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -414,9 +414,8 @@ fun HowWellScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (state.phase) {
|
||||
HowWellPhase.LOADING -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
HowWellPhase.LOADING -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
HowWellPhase.ERROR -> HowWellErrorScreen(
|
||||
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
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
||||
CloserHeartLoader(size = 48.dp)
|
||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||
Spacer(Modifier.weight(1f))
|
||||
OutlinedButton(
|
||||
|
|
@ -1202,9 +1201,8 @@ fun HowWellReplayScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (phase) {
|
||||
is HWReplayPhase.Loading -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
is HWReplayPhase.Loading -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
is HWReplayPhase.Error -> Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -231,7 +231,7 @@ fun MemoryLaneScreen(
|
|||
) {
|
||||
when (state.phase) {
|
||||
MemoryLanePhase.LOADING -> Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
||||
CloserHeartLoader()
|
||||
}
|
||||
MemoryLanePhase.LIST -> CapsuleListScreen(
|
||||
capsules = state.capsules,
|
||||
|
|
@ -494,7 +494,7 @@ private fun CapsuleCreateScreen(
|
|||
shape = RoundedCornerShape(18.dp),
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import androidx.compose.material.icons.filled.Person
|
|||
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||
import androidx.compose.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -255,7 +255,7 @@ private fun NameStep(
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -312,7 +312,7 @@ private fun SexStep(
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -440,7 +440,7 @@ private fun PhotoStep(
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ 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.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
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.AuthPrimaryDeep
|
||||
import app.closer.ui.components.BrandMessageRotator
|
||||
import app.closer.ui.components.CloserHeartLoader
|
||||
import app.closer.ui.theme.CloserPalette
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -85,11 +85,7 @@ fun OnboardingScreen(
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(36.dp),
|
||||
color = AuthPrimary,
|
||||
strokeWidth = 3.dp
|
||||
)
|
||||
CloserHeartLoader()
|
||||
BrandMessageRotator(color = AuthMuted)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||
import androidx.compose.material.icons.automirrored.filled.TrendingUp
|
||||
import androidx.compose.material3.Card
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -98,7 +98,7 @@ fun YourProgressScreen(
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
CloserHeartLoader()
|
||||
}
|
||||
}
|
||||
state.error != null -> {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -166,11 +166,7 @@ fun AcceptInviteScreen(
|
|||
contentColor = SettingsOnPrimary
|
||||
)
|
||||
) {
|
||||
if (state.isLoading) CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = SettingsOnPrimary,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
if (state.isLoading) CloserHeartLoader(size = 22.dp)
|
||||
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.Card
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -125,7 +125,7 @@ fun CreateInviteScreen(
|
|||
) {
|
||||
if (state.isLoading) {
|
||||
Spacer(Modifier.height(160.dp))
|
||||
CircularProgressIndicator(modifier = Modifier.size(40.dp))
|
||||
CloserHeartLoader(size = 64.dp)
|
||||
} else if (state.inviteCode != null) {
|
||||
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.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
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.CoupleRepository
|
||||
import app.closer.ui.components.BrandMessageRotator
|
||||
import app.closer.ui.components.CloserHeartLoader
|
||||
import app.closer.ui.components.StatusGlyph
|
||||
import app.closer.ui.settings.SettingsBackgroundBrush
|
||||
import app.closer.ui.settings.SettingsInk
|
||||
|
|
@ -181,7 +181,7 @@ fun EncryptionUpgradeScreen(
|
|||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
CircularProgressIndicator(color = SettingsPrimary)
|
||||
CloserHeartLoader()
|
||||
Spacer(Modifier.height(16.dp))
|
||||
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.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -107,7 +107,7 @@ fun InviteConfirmScreen(
|
|||
) {
|
||||
if (state.isLoading) {
|
||||
Spacer(Modifier.height(160.dp))
|
||||
CircularProgressIndicator(modifier = Modifier.size(40.dp))
|
||||
CloserHeartLoader(size = 64.dp)
|
||||
} else {
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
|
|
@ -156,11 +156,7 @@ fun InviteConfirmScreen(
|
|||
contentColor = SettingsOnPrimary
|
||||
)
|
||||
) {
|
||||
if (state.isConfirming) CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = SettingsOnPrimary,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
if (state.isConfirming) CloserHeartLoader(size = 22.dp)
|
||||
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.material3.Button
|
||||
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.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
|
|
@ -141,11 +141,7 @@ fun RecoveryScreen(
|
|||
)
|
||||
) {
|
||||
if (state.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = SettingsOnPrimary,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
CloserHeartLoader(size = 22.dp)
|
||||
} else {
|
||||
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.Card
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
|
|
@ -133,10 +133,7 @@ fun PaywallScreen(
|
|||
BenefitsCard()
|
||||
|
||||
if (uiState.purchaseState is BillingState.Loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(28.dp),
|
||||
color = CloserPalette.PurpleRich
|
||||
)
|
||||
CloserHeartLoader(size = 40.dp)
|
||||
}
|
||||
|
||||
ActionButtons(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
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.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -401,7 +401,7 @@ private fun CategoryLoadingCard() {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
||||
CloserHeartLoader(size = 32.dp)
|
||||
Text(
|
||||
text = "Loading prompts",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -424,7 +424,7 @@ private fun LoadingPackCard() {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFFB98AF4))
|
||||
CloserHeartLoader(size = 32.dp)
|
||||
Text(
|
||||
text = "Loading question packs",
|
||||
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.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -251,7 +251,7 @@ fun DeleteAccountScreen(
|
|||
)
|
||||
) {
|
||||
if (state.isReauthing) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.White, strokeWidth = 2.dp)
|
||||
CloserHeartLoader(size = 18.dp)
|
||||
} else {
|
||||
Text("Confirm", color = Color.White)
|
||||
}
|
||||
|
|
@ -328,7 +328,7 @@ fun DeleteAccountScreen(
|
|||
) {
|
||||
if (state.isDeleting) {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import androidx.compose.material.icons.filled.Person
|
|||
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||
import androidx.compose.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -175,7 +175,7 @@ fun EditProfileContent(
|
|||
) {
|
||||
if (state.isLoading) {
|
||||
Spacer(Modifier.height(64.dp))
|
||||
CircularProgressIndicator(color = SettingsPrimary)
|
||||
CloserHeartLoader()
|
||||
} else {
|
||||
val imageUri = state.photoUri ?: state.photoUrl.takeIf { it.isNotBlank() }
|
||||
Box(
|
||||
|
|
@ -356,7 +356,7 @@ fun EditProfileContent(
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
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.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -190,7 +190,7 @@ fun RelationshipSettingsScreen(
|
|||
) {
|
||||
if (state.isLeaving) {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -226,7 +226,7 @@ fun SettingsScreen(
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
CloserHeartLoader()
|
||||
}
|
||||
} else {
|
||||
var showBaselineOutcomeDialog by remember { mutableStateOf(false) }
|
||||
|
|
@ -499,11 +499,7 @@ fun SettingsScreen(
|
|||
contentColor = SettingsDanger
|
||||
)
|
||||
) {
|
||||
if (state.isSigningOut) CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = SettingsDanger,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
if (state.isSigningOut) CloserHeartLoader(size = 22.dp)
|
||||
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.Card
|
||||
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.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -417,9 +417,8 @@ fun ThisOrThatScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (state.phase) {
|
||||
TotPhase.LOADING -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
TotPhase.LOADING -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
TotPhase.ERROR -> ErrorState(
|
||||
message = state.error ?: "Something didn't load. Go back and try again.",
|
||||
|
|
@ -447,9 +446,8 @@ fun ThisOrThatScreen(
|
|||
TotPhase.PLAYING -> {
|
||||
val question = state.questions.getOrNull(state.currentIndex)
|
||||
if (question == null) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
} else {
|
||||
val config = question.answerConfig as? ThisOrThatAnswerConfigImpl
|
||||
|
|
@ -909,7 +907,7 @@ private fun WaitingForRevealScreen(
|
|||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
||||
CloserHeartLoader(size = 48.dp)
|
||||
BrandMessageRotator(style = MaterialTheme.typography.bodySmall)
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
|
@ -1272,9 +1270,8 @@ fun ThisOrThatReplayScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (phase) {
|
||||
is TotReplayPhase.Loading -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
is TotReplayPhase.Loading -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
is TotReplayPhase.Error -> Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ 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 app.closer.ui.components.CloserHeartLoader
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -104,7 +104,7 @@ private fun CategoryPickerContent(
|
|||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
||||
CloserHeartLoader(size = 28.dp)
|
||||
Text(
|
||||
"Loading categories…",
|
||||
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.material3.Button
|
||||
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.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -262,7 +262,7 @@ private fun SpinWheelContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
state.isLoading -> CircularProgressIndicator(color = CloserPalette.PurpleDeep)
|
||||
state.isLoading -> CloserHeartLoader()
|
||||
else -> {
|
||||
Text(
|
||||
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.Card
|
||||
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.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -197,9 +197,8 @@ fun WheelCompleteScreen(
|
|||
.background(closerBackgroundBrush())
|
||||
) {
|
||||
when (state.phase) {
|
||||
WheelRevealPhase.LOADING -> CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
color = CloserPalette.PurpleDeep
|
||||
WheelRevealPhase.LOADING -> CloserHeartLoader(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
WheelRevealPhase.WAITING -> WheelWaitingContent(
|
||||
partnerName = state.partnerName,
|
||||
|
|
@ -254,7 +253,7 @@ private fun WheelWaitingContent(
|
|||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
CircularProgressIndicator(color = CloserPalette.PurpleDeep, strokeWidth = 3.dp)
|
||||
CloserHeartLoader(size = 48.dp)
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import androidx.compose.material3.Card
|
|||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
|
|
@ -114,7 +114,7 @@ private fun WheelSessionContent(
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(color = Color(0xFF56306F))
|
||||
CloserHeartLoader()
|
||||
}
|
||||
return@Column
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ struct LoadingView: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(spacing: CloserSpacing.lg) {
|
||||
ProgressView()
|
||||
.tint(.closerPrimary)
|
||||
.scaleEffect(1.2)
|
||||
CloserHeartLoader()
|
||||
Text(message)
|
||||
.font(CloserFont.callout)
|
||||
.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
|
||||
|
||||
struct ErrorView: View {
|
||||
|
|
|
|||
Loading…
Reference in New Issue