diff --git a/app/src/main/java/app/closer/ui/answers/AnswerHistoryScreen.kt b/app/src/main/java/app/closer/ui/answers/AnswerHistoryScreen.kt index 67148c67..af1a9417 100644 --- a/app/src/main/java/app/closer/ui/answers/AnswerHistoryScreen.kt +++ b/app/src/main/java/app/closer/ui/answers/AnswerHistoryScreen.kt @@ -2,6 +2,7 @@ package app.closer.ui.answers import app.closer.ui.theme.closerBackgroundBrush import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -19,11 +20,15 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Visibility import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -48,8 +53,13 @@ import app.closer.core.navigation.AppRoute import app.closer.domain.model.LocalAnswer import app.closer.ui.components.CategoryGlyph import app.closer.ui.components.EmptyState +import app.closer.ui.theme.CloserPalette import app.closer.ui.questions.displayCategoryName +private enum class AnswerHistoryFilter { + ALL, PRIVATE, REVEALED +} + @Composable fun AnswerHistoryScreen( onNavigate: (String) -> Unit = {}, @@ -73,6 +83,16 @@ private fun AnswerHistoryContent( onDelete: (String) -> Unit ) { var pendingDelete by remember { mutableStateOf(null) } + var selectedFilter by remember { mutableStateOf(AnswerHistoryFilter.ALL) } + val visibleAnswers = remember(state.answers, selectedFilter) { + state.answers.filter { answer -> + when (selectedFilter) { + AnswerHistoryFilter.ALL -> true + AnswerHistoryFilter.PRIVATE -> !answer.isRevealed + AnswerHistoryFilter.REVEALED -> answer.isRevealed + } + } + } pendingDelete?.let { answer -> AlertDialog( @@ -146,7 +166,25 @@ private fun AnswerHistoryContent( ) } } else { - items(state.answers, key = { it.questionId }) { answer -> + item { + HistoryFilterRow( + selected = selectedFilter, + privateCount = state.answers.count { !it.isRevealed }, + revealedCount = state.answers.count { it.isRevealed }, + onSelected = { selectedFilter = it } + ) + } + + if (visibleAnswers.isEmpty()) { + item { + EmptyState( + title = "Nothing in this view", + body = "Switch filters to see the rest of your saved answers." + ) + } + } + + items(visibleAnswers, key = { it.questionId }) { answer -> AnswerHistoryCard( answer = answer, onClick = { onAnswerSelected(answer) }, @@ -158,6 +196,69 @@ private fun AnswerHistoryContent( } } +@Composable +private fun HistoryFilterRow( + selected: AnswerHistoryFilter, + privateCount: Int, + revealedCount: Int, + onSelected: (AnswerHistoryFilter) -> Unit +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + HistoryFilterPill( + label = "All", + count = privateCount + revealedCount, + selected = selected == AnswerHistoryFilter.ALL, + onClick = { onSelected(AnswerHistoryFilter.ALL) }, + modifier = Modifier.weight(1f) + ) + HistoryFilterPill( + label = "Private", + count = privateCount, + selected = selected == AnswerHistoryFilter.PRIVATE, + onClick = { onSelected(AnswerHistoryFilter.PRIVATE) }, + modifier = Modifier.weight(1f) + ) + HistoryFilterPill( + label = "Revealed", + count = revealedCount, + selected = selected == AnswerHistoryFilter.REVEALED, + onClick = { onSelected(AnswerHistoryFilter.REVEALED) }, + modifier = Modifier.weight(1f) + ) + } +} + +@Composable +private fun HistoryFilterPill( + label: String, + count: Int, + selected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Surface( + modifier = modifier + .heightIn(min = 44.dp) + .clickable(onClick = onClick), + shape = RoundedCornerShape(999.dp), + color = if (selected) CloserPalette.PurpleMist else Color.White.copy(alpha = 0.74f), + shadowElevation = if (selected) 2.dp else 0.dp + ) { + Text( + text = "$label $count", + modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp), + style = MaterialTheme.typography.labelMedium, + color = if (selected) CloserPalette.PurpleDeep else MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } +} + @OptIn(ExperimentalLayoutApi::class) @Composable @@ -192,7 +293,7 @@ private fun AnswerHistoryCard( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - HistoryPill(if (answer.isRevealed) "Revealed" else "Private") + HistoryStateBadge(isRevealed = answer.isRevealed) HistoryPill(answer.category.displayCategoryName()) } Text( @@ -235,6 +336,35 @@ private fun AnswerHistoryCard( } } +@Composable +private fun HistoryStateBadge(isRevealed: Boolean) { + val tint = if (isRevealed) CloserPalette.PurpleDeep else CloserPalette.Evergreen + val container = if (isRevealed) CloserPalette.PurpleMist else CloserPalette.Evergreen.copy(alpha = 0.10f) + Surface( + shape = RoundedCornerShape(999.dp), + color = container + ) { + Row( + modifier = Modifier.padding(horizontal = 10.dp, vertical = 7.dp), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = if (isRevealed) Icons.Filled.Visibility else Icons.Filled.Lock, + contentDescription = null, + tint = tint, + modifier = Modifier.size(14.dp) + ) + Text( + text = if (isRevealed) "Revealed" else "Private", + style = MaterialTheme.typography.labelMedium, + color = tint, + fontWeight = FontWeight.SemiBold + ) + } + } +} + @Composable private fun HistoryPill(label: String) { Surface( diff --git a/app/src/main/java/app/closer/ui/desiresync/DesireSyncScreen.kt b/app/src/main/java/app/closer/ui/desiresync/DesireSyncScreen.kt index b6d41138..78ab4a7a 100644 --- a/app/src/main/java/app/closer/ui/desiresync/DesireSyncScreen.kt +++ b/app/src/main/java/app/closer/ui/desiresync/DesireSyncScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -21,6 +22,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.draw.clip import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.FavoriteBorder @@ -31,7 +33,6 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Surface @@ -499,11 +500,9 @@ private fun DSAnswerScreen( } } - LinearProgressIndicator( - progress = { index.toFloat() / total }, - modifier = Modifier.fillMaxWidth(), - color = CloserPalette.Romantic, - trackColor = CloserPalette.Romantic.copy(alpha = 0.15f) + DesireProgressPill( + progress = index.toFloat() / total, + modifier = Modifier.fillMaxWidth() ) Card( @@ -605,6 +604,7 @@ private fun DSRevealScreen( textAlign = TextAlign.Center ) } + DesireRevealMeter(matches = matches.size, total = total) } } @@ -660,6 +660,116 @@ private fun DSRevealScreen( } } +@Composable +private fun DesireProgressPill( + progress: Float, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .height(8.dp) + .clip(RoundedCornerShape(999.dp)) + .background(CloserPalette.Romantic.copy(alpha = 0.15f)) + ) { + Box( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth(progress.coerceIn(0f, 1f)) + .clip(RoundedCornerShape(999.dp)) + .background(CloserPalette.Romantic) + ) + } +} + +@Composable +private fun DesireRevealMeter( + matches: Int, + total: Int +) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(22.dp), + colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.86f)), + elevation = CardDefaults.cardElevation(0.dp) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + DesirePrivacyTile( + label = "Partner A", + value = "$total private", + modifier = Modifier.weight(1f) + ) + StatusGlyph( + icon = Icons.Filled.Sync, + tint = CloserPalette.Romantic, + container = CloserPalette.Romantic.copy(alpha = 0.12f), + size = 38.dp, + iconSize = 20.dp + ) + DesirePrivacyTile( + label = "Partner B", + value = "$total private", + modifier = Modifier.weight(1f) + ) + } + DesireProgressPill( + progress = matches.toFloat() / total.coerceAtLeast(1), + modifier = Modifier.fillMaxWidth() + ) + Text( + text = "$matches shared, ${total - matches} kept private", + style = MaterialTheme.typography.labelMedium, + color = CloserPalette.Romantic, + fontWeight = FontWeight.SemiBold, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + } +} + +@Composable +private fun DesirePrivacyTile( + label: String, + value: String, + modifier: Modifier = Modifier +) { + Surface( + modifier = modifier, + shape = RoundedCornerShape(16.dp), + color = CloserPalette.Romantic.copy(alpha = 0.08f) + ) { + Column( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + Text( + text = label, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = value, + style = MaterialTheme.typography.labelMedium, + color = CloserPalette.Romantic, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } +} + @Composable private fun DesireMatchCard(match: DesireMatch) { Card( diff --git a/app/src/main/java/app/closer/ui/howwell/HowWellScreen.kt b/app/src/main/java/app/closer/ui/howwell/HowWellScreen.kt index 8599e62e..67f5f14e 100644 --- a/app/src/main/java/app/closer/ui/howwell/HowWellScreen.kt +++ b/app/src/main/java/app/closer/ui/howwell/HowWellScreen.kt @@ -2,10 +2,12 @@ package app.closer.ui.howwell import app.closer.ui.theme.closerCardColor import android.util.Log +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -20,6 +22,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.draw.clip import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Psychology @@ -30,7 +33,6 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Surface @@ -43,6 +45,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -502,11 +506,9 @@ private fun AnswerScreen( } } - LinearProgressIndicator( - progress = { index.toFloat() / total }, - modifier = Modifier.fillMaxWidth(), - color = CloserPalette.PurpleDeep, - trackColor = CloserPalette.PurpleMist + HowWellProgressPill( + progress = index.toFloat() / total, + modifier = Modifier.fillMaxWidth() ) Card( @@ -656,6 +658,12 @@ private fun RevealScreen( } } + HowWellScoreStrip( + score = score, + answered = questionNumber, + total = total + ) + Text( text = result.question.text, style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), @@ -755,18 +763,10 @@ private fun CompleteScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(10.dp) ) { - StatusGlyph( - icon = Icons.Filled.Timeline, - tint = CloserPalette.PurpleDeep, - container = CloserPalette.PurpleMist, - size = 82.dp, - iconSize = 40.dp - ) - Text( - text = "$score / $total", - style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold), - color = CloserPalette.PurpleDeep, - textAlign = TextAlign.Center + HowWellScoreRing( + score = score, + total = total, + modifier = Modifier.size(118.dp) ) Text( text = scoreLabel(score, total), @@ -808,6 +808,110 @@ private fun CompleteScreen( } } +@Composable +private fun HowWellProgressPill( + progress: Float, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .height(8.dp) + .clip(RoundedCornerShape(999.dp)) + .background(CloserPalette.PurpleMist) + ) { + Box( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth(progress.coerceIn(0f, 1f)) + .clip(RoundedCornerShape(999.dp)) + .background(CloserPalette.PurpleDeep) + ) + } +} + +@Composable +private fun HowWellScoreStrip( + score: Int, + answered: Int, + total: Int +) { + Surface( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(18.dp), + color = CloserPalette.PurpleMist + ) { + Row( + modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + HowWellProgressPill( + progress = score.toFloat() / answered.coerceAtLeast(1), + modifier = Modifier.weight(1f) + ) + Text( + text = "$score / $answered read", + style = MaterialTheme.typography.labelMedium, + color = CloserPalette.PurpleDeep, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "$total total", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } +} + +@Composable +private fun HowWellScoreRing( + score: Int, + total: Int, + modifier: Modifier = Modifier +) { + val progress = if (total == 0) 0f else score.toFloat() / total + Box( + modifier = modifier, + contentAlignment = Alignment.Center + ) { + Canvas(modifier = Modifier.fillMaxSize()) { + val strokeWidth = 11.dp.toPx() + drawArc( + color = CloserPalette.PurpleMist, + startAngle = -90f, + sweepAngle = 360f, + useCenter = false, + style = Stroke(width = strokeWidth, cap = StrokeCap.Round) + ) + drawArc( + color = CloserPalette.PurpleDeep, + startAngle = -90f, + sweepAngle = 360f * progress.coerceIn(0f, 1f), + useCenter = false, + style = Stroke(width = strokeWidth, cap = StrokeCap.Round) + ) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = "$score", + style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold), + color = CloserPalette.PurpleDeep + ) + Text( + text = "of $total", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = FontWeight.SemiBold + ) + } + } +} + @Composable private fun BreakdownRow(result: HowWellResult) { val matchColor = Color(0xFF2E7D32) diff --git a/app/src/main/java/app/closer/ui/paywall/PaywallScreen.kt b/app/src/main/java/app/closer/ui/paywall/PaywallScreen.kt index 507d9d48..6934476b 100644 --- a/app/src/main/java/app/closer/ui/paywall/PaywallScreen.kt +++ b/app/src/main/java/app/closer/ui/paywall/PaywallScreen.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -25,6 +27,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Star import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card @@ -60,6 +63,8 @@ import app.closer.core.navigation.ExternalLinks import app.closer.domain.repository.BillingState import app.closer.ui.components.ErrorState import app.closer.ui.components.LoadingState +import app.closer.ui.components.StatusGlyph +import app.closer.ui.theme.CloserPalette import com.revenuecat.purchases.Package private val BENEFITS = listOf( @@ -103,7 +108,6 @@ fun PaywallScreen( horizontalAlignment = Alignment.CenterHorizontally ) { HeaderSection(onClose = { onNavigate("back") }) - BenefitsCard() when { uiState.isLoading -> LoadingState( @@ -124,10 +128,12 @@ fun PaywallScreen( ) } + BenefitsCard() + if (uiState.purchaseState is BillingState.Loading) { CircularProgressIndicator( modifier = Modifier.size(28.dp), - color = Color(0xFFB98AF4) + color = CloserPalette.PurpleRich ) } @@ -163,9 +169,16 @@ private fun HeaderSection( ) { Row( modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + horizontalArrangement = Arrangement.spacedBy(14.dp), + verticalAlignment = Alignment.Top ) { + StatusGlyph( + icon = Icons.Filled.Star, + tint = CloserPalette.PurpleDeep, + container = CloserPalette.PurpleGlow, + size = 58.dp, + iconSize = 28.dp + ) Column(modifier = Modifier.weight(1f)) { Text( text = "Go deeper together", @@ -180,7 +193,7 @@ private fun HeaderSection( ) } - TextButton(onClick = onClose) { + TextButton(onClick = onClose, modifier = Modifier.size(48.dp)) { Icon( imageVector = Icons.Default.Close, contentDescription = "Close", @@ -190,45 +203,61 @@ private fun HeaderSection( } } +@OptIn(ExperimentalLayoutApi::class) @Composable private fun BenefitsCard(modifier: Modifier = Modifier) { Card( modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(22.dp), colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.88f)), - elevation = CardDefaults.cardElevation(defaultElevation = 10.dp) + elevation = CardDefaults.cardElevation(defaultElevation = 3.dp) ) { Column( - modifier = Modifier.padding(24.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + modifier = Modifier.padding(18.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) ) { Text( text = "What's included", style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), color = MaterialTheme.colorScheme.onSurface ) - BENEFITS.forEach { benefit -> - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = null, - tint = Color(0xFF56306F), - modifier = Modifier.size(18.dp) - ) - Text( - text = benefit, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface - ) + FlowRow( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + BENEFITS.forEach { benefit -> + BenefitPill(benefit) } } } } } +@Composable +private fun BenefitPill(label: String) { + Row( + modifier = Modifier + .clip(RoundedCornerShape(999.dp)) + .background(CloserPalette.PurpleMist) + .padding(horizontal = 10.dp, vertical = 7.dp), + horizontalArrangement = Arrangement.spacedBy(7.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null, + tint = CloserPalette.PurpleDeep, + modifier = Modifier.size(15.dp) + ) + Text( + text = label, + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurface + ) + } +} + @Composable private fun PlanOptions( packages: List, @@ -239,8 +268,8 @@ private fun PlanOptions( Card( modifier = modifier.fillMaxWidth(), shape = RoundedCornerShape(28.dp), - colors = CardDefaults.cardColors(containerColor = Color(0xFFF4E8FF)), - elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) + colors = CardDefaults.cardColors(containerColor = CloserPalette.PurpleSoft), + elevation = CardDefaults.cardElevation(defaultElevation = 8.dp) ) { Column( modifier = Modifier.padding(22.dp), @@ -251,6 +280,11 @@ private fun PlanOptions( style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), color = MaterialTheme.colorScheme.onSurface ) + Text( + text = "Plans are selected here. Restore and legal links stay below the purchase action.", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) if (packages.isEmpty()) { Text( @@ -284,7 +318,7 @@ private fun PlanRow( .fillMaxWidth() .clip(RoundedCornerShape(18.dp)) .background( - if (isSelected) Color(0xFFB98AF4).copy(alpha = 0.20f) + if (isSelected) CloserPalette.PurpleRich.copy(alpha = 0.20f) else Color.White.copy(alpha = 0.64f) ) .selectable( @@ -344,20 +378,41 @@ private fun ActionButtons( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color(0xFFB98AF4), + containerColor = CloserPalette.PurpleDeep, contentColor = MaterialTheme.colorScheme.onPrimary, - disabledContainerColor = Color(0xFFB98AF4).copy(alpha = 0.40f), + disabledContainerColor = CloserPalette.PurpleRich.copy(alpha = 0.40f), disabledContentColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.54f) ) ) { Text("Continue", fontWeight = FontWeight.SemiBold) } - TextButton(onClick = onRestore) { - Text( - text = "Restore purchases", - color = Color(0xFF9B8AA6) - ) + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.54f)), + elevation = CardDefaults.cardElevation(0.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Already subscribed?", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + TextButton(onClick = onRestore) { + Text( + text = "Restore", + color = CloserPalette.PurpleDeep, + fontWeight = FontWeight.SemiBold + ) + } + } } } } diff --git a/app/src/main/java/app/closer/ui/questions/QuestionCategoryScreen.kt b/app/src/main/java/app/closer/ui/questions/QuestionCategoryScreen.kt index 58504c1b..3c3c2fba 100644 --- a/app/src/main/java/app/closer/ui/questions/QuestionCategoryScreen.kt +++ b/app/src/main/java/app/closer/ui/questions/QuestionCategoryScreen.kt @@ -288,23 +288,50 @@ private fun FilterPill( @Composable private fun DepthHeader(depth: Int, count: Int) { - Row( + Surface( modifier = Modifier .fillMaxWidth() .padding(top = 6.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + shape = RoundedCornerShape(18.dp), + color = Color.White.copy(alpha = 0.68f), + shadowElevation = 0.dp ) { - Text( - text = "Depth $depth", - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.SemiBold - ) - CategoryPill("$count ${if (count == 1) "prompt" else "prompts"}") + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 11.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + Text( + text = depthLabel(depth), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "Depth $depth", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + CategoryPill("$count ${if (count == 1) "prompt" else "prompts"}", emphasis = true) + } } } +private fun depthLabel(depth: Int): String = when (depth) { + 1 -> "Light openers" + 2 -> "Closer prompts" + 3 -> "Deeper conversation" + else -> "Depth $depth" +} + @Composable private fun QuestionListCard( question: Question,