feat: add answer history filter pills with reveal/private badges, DesireSync progress meter and reveal UI, HowWell ring progress and privacy tiles, Paywall feature scavenger hunt, category screen grid polish
This commit is contained in:
parent
15c1fbdda0
commit
606d724f12
|
|
@ -2,6 +2,7 @@ package app.closer.ui.answers
|
||||||
|
|
||||||
import app.closer.ui.theme.closerBackgroundBrush
|
import app.closer.ui.theme.closerBackgroundBrush
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
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.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.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
|
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
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -48,8 +53,13 @@ import app.closer.core.navigation.AppRoute
|
||||||
import app.closer.domain.model.LocalAnswer
|
import app.closer.domain.model.LocalAnswer
|
||||||
import app.closer.ui.components.CategoryGlyph
|
import app.closer.ui.components.CategoryGlyph
|
||||||
import app.closer.ui.components.EmptyState
|
import app.closer.ui.components.EmptyState
|
||||||
|
import app.closer.ui.theme.CloserPalette
|
||||||
import app.closer.ui.questions.displayCategoryName
|
import app.closer.ui.questions.displayCategoryName
|
||||||
|
|
||||||
|
private enum class AnswerHistoryFilter {
|
||||||
|
ALL, PRIVATE, REVEALED
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnswerHistoryScreen(
|
fun AnswerHistoryScreen(
|
||||||
onNavigate: (String) -> Unit = {},
|
onNavigate: (String) -> Unit = {},
|
||||||
|
|
@ -73,6 +83,16 @@ private fun AnswerHistoryContent(
|
||||||
onDelete: (String) -> Unit
|
onDelete: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
var pendingDelete by remember { mutableStateOf<LocalAnswer?>(null) }
|
var pendingDelete by remember { mutableStateOf<LocalAnswer?>(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 ->
|
pendingDelete?.let { answer ->
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
|
@ -146,7 +166,25 @@ private fun AnswerHistoryContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} 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(
|
AnswerHistoryCard(
|
||||||
answer = answer,
|
answer = answer,
|
||||||
onClick = { onAnswerSelected(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)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -192,7 +293,7 @@ private fun AnswerHistoryCard(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
HistoryPill(if (answer.isRevealed) "Revealed" else "Private")
|
HistoryStateBadge(isRevealed = answer.isRevealed)
|
||||||
HistoryPill(answer.category.displayCategoryName())
|
HistoryPill(answer.category.displayCategoryName())
|
||||||
}
|
}
|
||||||
Text(
|
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
|
@Composable
|
||||||
private fun HistoryPill(label: String) {
|
private fun HistoryPill(label: String) {
|
||||||
Surface(
|
Surface(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
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.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
|
||||||
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
|
||||||
|
|
@ -499,11 +500,9 @@ private fun DSAnswerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearProgressIndicator(
|
DesireProgressPill(
|
||||||
progress = { index.toFloat() / total },
|
progress = index.toFloat() / total,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth()
|
||||||
color = CloserPalette.Romantic,
|
|
||||||
trackColor = CloserPalette.Romantic.copy(alpha = 0.15f)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -605,6 +604,7 @@ private fun DSRevealScreen(
|
||||||
textAlign = TextAlign.Center
|
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
|
@Composable
|
||||||
private fun DesireMatchCard(match: DesireMatch) {
|
private fun DesireMatchCard(match: DesireMatch) {
|
||||||
Card(
|
Card(
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package app.closer.ui.howwell
|
||||||
|
|
||||||
import app.closer.ui.theme.closerCardColor
|
import app.closer.ui.theme.closerCardColor
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.lazy.items
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.Psychology
|
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.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
|
||||||
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
|
||||||
|
|
@ -43,6 +45,8 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
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.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -502,11 +506,9 @@ private fun AnswerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearProgressIndicator(
|
HowWellProgressPill(
|
||||||
progress = { index.toFloat() / total },
|
progress = index.toFloat() / total,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth()
|
||||||
color = CloserPalette.PurpleDeep,
|
|
||||||
trackColor = CloserPalette.PurpleMist
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -656,6 +658,12 @@ private fun RevealScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HowWellScoreStrip(
|
||||||
|
score = score,
|
||||||
|
answered = questionNumber,
|
||||||
|
total = total
|
||||||
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = result.question.text,
|
text = result.question.text,
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
||||||
|
|
@ -755,18 +763,10 @@ private fun CompleteScreen(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
) {
|
) {
|
||||||
StatusGlyph(
|
HowWellScoreRing(
|
||||||
icon = Icons.Filled.Timeline,
|
score = score,
|
||||||
tint = CloserPalette.PurpleDeep,
|
total = total,
|
||||||
container = CloserPalette.PurpleMist,
|
modifier = Modifier.size(118.dp)
|
||||||
size = 82.dp,
|
|
||||||
iconSize = 40.dp
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "$score / $total",
|
|
||||||
style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold),
|
|
||||||
color = CloserPalette.PurpleDeep,
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = scoreLabel(score, total),
|
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
|
@Composable
|
||||||
private fun BreakdownRow(result: HowWellResult) {
|
private fun BreakdownRow(result: HowWellResult) {
|
||||||
val matchColor = Color(0xFF2E7D32)
|
val matchColor = Color(0xFF2E7D32)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
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.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Star
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
|
@ -60,6 +63,8 @@ import app.closer.core.navigation.ExternalLinks
|
||||||
import app.closer.domain.repository.BillingState
|
import app.closer.domain.repository.BillingState
|
||||||
import app.closer.ui.components.ErrorState
|
import app.closer.ui.components.ErrorState
|
||||||
import app.closer.ui.components.LoadingState
|
import app.closer.ui.components.LoadingState
|
||||||
|
import app.closer.ui.components.StatusGlyph
|
||||||
|
import app.closer.ui.theme.CloserPalette
|
||||||
import com.revenuecat.purchases.Package
|
import com.revenuecat.purchases.Package
|
||||||
|
|
||||||
private val BENEFITS = listOf(
|
private val BENEFITS = listOf(
|
||||||
|
|
@ -103,7 +108,6 @@ fun PaywallScreen(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
HeaderSection(onClose = { onNavigate("back") })
|
HeaderSection(onClose = { onNavigate("back") })
|
||||||
BenefitsCard()
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
uiState.isLoading -> LoadingState(
|
uiState.isLoading -> LoadingState(
|
||||||
|
|
@ -124,10 +128,12 @@ fun PaywallScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BenefitsCard()
|
||||||
|
|
||||||
if (uiState.purchaseState is BillingState.Loading) {
|
if (uiState.purchaseState is BillingState.Loading) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.size(28.dp),
|
modifier = Modifier.size(28.dp),
|
||||||
color = Color(0xFFB98AF4)
|
color = CloserPalette.PurpleRich
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,9 +169,16 @@ private fun HeaderSection(
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
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)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
text = "Go deeper together",
|
text = "Go deeper together",
|
||||||
|
|
@ -180,7 +193,7 @@ private fun HeaderSection(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextButton(onClick = onClose) {
|
TextButton(onClick = onClose, modifier = Modifier.size(48.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Close,
|
imageVector = Icons.Default.Close,
|
||||||
contentDescription = "Close",
|
contentDescription = "Close",
|
||||||
|
|
@ -190,43 +203,59 @@ private fun HeaderSection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun BenefitsCard(modifier: Modifier = Modifier) {
|
private fun BenefitsCard(modifier: Modifier = Modifier) {
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
shape = RoundedCornerShape(28.dp),
|
shape = RoundedCornerShape(22.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.88f)),
|
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.88f)),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp)
|
elevation = CardDefaults.cardElevation(defaultElevation = 3.dp)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(24.dp),
|
modifier = Modifier.padding(18.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "What's included",
|
text = "What's included",
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
)
|
)
|
||||||
|
FlowRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
BENEFITS.forEach { benefit ->
|
BENEFITS.forEach { benefit ->
|
||||||
|
BenefitPill(benefit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BenefitPill(label: String) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(999.dp))
|
||||||
|
.background(CloserPalette.PurpleMist)
|
||||||
|
.padding(horizontal = 10.dp, vertical = 7.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(7.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color(0xFF56306F),
|
tint = CloserPalette.PurpleDeep,
|
||||||
modifier = Modifier.size(18.dp)
|
modifier = Modifier.size(15.dp)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = benefit,
|
text = label,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -239,8 +268,8 @@ private fun PlanOptions(
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
shape = RoundedCornerShape(28.dp),
|
shape = RoundedCornerShape(28.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = Color(0xFFF4E8FF)),
|
colors = CardDefaults.cardColors(containerColor = CloserPalette.PurpleSoft),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
|
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(22.dp),
|
modifier = Modifier.padding(22.dp),
|
||||||
|
|
@ -251,6 +280,11 @@ private fun PlanOptions(
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
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()) {
|
if (packages.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -284,7 +318,7 @@ private fun PlanRow(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clip(RoundedCornerShape(18.dp))
|
.clip(RoundedCornerShape(18.dp))
|
||||||
.background(
|
.background(
|
||||||
if (isSelected) Color(0xFFB98AF4).copy(alpha = 0.20f)
|
if (isSelected) CloserPalette.PurpleRich.copy(alpha = 0.20f)
|
||||||
else Color.White.copy(alpha = 0.64f)
|
else Color.White.copy(alpha = 0.64f)
|
||||||
)
|
)
|
||||||
.selectable(
|
.selectable(
|
||||||
|
|
@ -344,22 +378,43 @@ private fun ActionButtons(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = Color(0xFFB98AF4),
|
containerColor = CloserPalette.PurpleDeep,
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
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)
|
disabledContentColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.54f)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text("Continue", fontWeight = FontWeight.SemiBold)
|
Text("Continue", fontWeight = FontWeight.SemiBold)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
TextButton(onClick = onRestore) {
|
||||||
Text(
|
Text(
|
||||||
text = "Restore purchases",
|
text = "Restore",
|
||||||
color = Color(0xFF9B8AA6)
|
color = CloserPalette.PurpleDeep,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -288,21 +288,48 @@ private fun FilterPill(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DepthHeader(depth: Int, count: Int) {
|
private fun DepthHeader(depth: Int, count: Int) {
|
||||||
Row(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 6.dp),
|
.padding(top = 6.dp),
|
||||||
|
shape = RoundedCornerShape(18.dp),
|
||||||
|
color = Color.White.copy(alpha = 0.68f),
|
||||||
|
shadowElevation = 0.dp
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 14.dp, vertical = 11.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
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(
|
||||||
text = "Depth $depth",
|
text = "Depth $depth",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
fontWeight = FontWeight.SemiBold
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
CategoryPill("$count ${if (count == 1) "prompt" else "prompts"}")
|
|
||||||
}
|
}
|
||||||
|
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
|
@Composable
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue