feat: add CategoryGlyph component, DesireSync polish, UI refinements across screens

This commit is contained in:
null 2026-06-17 22:31:34 -05:00
parent 85bb8d9f69
commit d135b306aa
13 changed files with 469 additions and 127 deletions

View File

@ -21,7 +21,7 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
AppNavigation(startDestination = app.closer.core.navigation.AppRoute.PLAY)
AppNavigation()
}
}
}

View File

@ -4,6 +4,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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -12,6 +14,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
@ -30,6 +33,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
@ -41,6 +45,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
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.questions.displayCategoryName
@ -157,6 +162,7 @@ private fun AnswerHistoryContent(
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun AnswerHistoryCard(
answer: LocalAnswer,
@ -170,47 +176,62 @@ private fun AnswerHistoryCard(
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
) {
Column(
Row(
modifier = Modifier.padding(17.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.Top
) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
HistoryPill(if (answer.isRevealed) "Revealed" else "Private")
HistoryPill(answer.category.displayCategoryName())
}
Text(
text = answer.questionText,
style = MaterialTheme.typography.titleMedium,
color = Color(0xFF261D2E),
fontWeight = FontWeight.SemiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis
CategoryGlyph(
categoryId = answer.category,
modifier = Modifier.size(46.dp),
size = 46.dp,
iconSize = 22.dp
)
Text(
text = if (answer.isRevealed) answer.revealSummary() else "Saved privately. Tap to reveal.",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF5A5060),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
HistoryPill(if (answer.isRevealed) "Revealed" else "Private")
HistoryPill(answer.category.displayCategoryName())
}
Text(
text = if (answer.isRevealed) "Opened" else "Private",
style = MaterialTheme.typography.labelMedium,
color = Color(0xFF56306F),
fontWeight = FontWeight.SemiBold
text = answer.questionText,
style = MaterialTheme.typography.titleMedium,
color = Color(0xFF261D2E),
fontWeight = FontWeight.SemiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
TextButton(
onClick = onDelete,
modifier = Modifier.heightIn(min = 48.dp)
Text(
text = if (answer.isRevealed) answer.revealSummary() else "Saved privately. Tap to reveal.",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF5A5060),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Remove",
color = Color(0xFF8D2D35)
text = if (answer.isRevealed) "Opened" else "Private",
style = MaterialTheme.typography.labelMedium,
color = Color(0xFF56306F),
fontWeight = FontWeight.SemiBold
)
TextButton(
onClick = onDelete,
modifier = Modifier.heightIn(min = 48.dp)
) {
Text(
text = "Remove",
color = Color(0xFF8D2D35)
)
}
}
}
}

View File

@ -17,6 +17,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
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
@ -47,6 +48,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.ui.components.StatusGlyph
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -97,7 +99,11 @@ fun ForgotPasswordScreen(
if (state.sent) {
Spacer(Modifier.height(48.dp))
Text("", style = MaterialTheme.typography.displayMedium, color = AuthPrimaryDeep)
StatusGlyph(
icon = Icons.Filled.Check,
tint = AuthPrimaryDeep,
container = AuthPrimary.copy(alpha = 0.16f)
)
Spacer(Modifier.height(16.dp))
Text(
"Reset email sent",

View File

@ -0,0 +1,239 @@
package app.closer.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.filled.AttachMoney
import androidx.compose.material.icons.filled.CalendarToday
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.People
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Psychology
import androidx.compose.material.icons.filled.QuestionAnswer
import androidx.compose.material.icons.filled.Shield
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.Timeline
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import app.closer.ui.theme.CloserPalette
data class GlyphStyle(
val icon: ImageVector,
val tint: Color,
val container: Color
)
@Composable
fun CategoryGlyph(
categoryId: String,
modifier: Modifier = Modifier,
iconName: String? = null,
locked: Boolean = false,
size: Dp = 48.dp,
iconSize: Dp = 23.dp
) {
val style = categoryGlyphStyle(categoryId = categoryId, iconName = iconName, locked = locked)
Surface(
modifier = modifier.size(size),
shape = RoundedCornerShape((size.value * 0.36f).dp),
color = style.container
) {
Box(contentAlignment = Alignment.Center) {
Icon(
imageVector = style.icon,
contentDescription = null,
tint = style.tint,
modifier = Modifier.size(iconSize)
)
}
}
}
@Composable
fun StatusGlyph(
icon: ImageVector,
modifier: Modifier = Modifier,
tint: Color = CloserPalette.PurpleDeep,
container: Color = CloserPalette.PurpleMist,
size: Dp = 72.dp,
iconSize: Dp = 34.dp,
shadowElevation: Dp = 0.dp
) {
Surface(
modifier = modifier.size(size),
shape = RoundedCornerShape((size.value * 0.34f).dp),
color = container,
shadowElevation = shadowElevation
) {
Box(contentAlignment = Alignment.Center) {
Icon(
imageVector = icon,
contentDescription = null,
tint = tint,
modifier = Modifier.size(iconSize)
)
}
}
}
@Composable
fun ResultGlyph(
isPositive: Boolean,
modifier: Modifier = Modifier,
isClose: Boolean = false,
size: Dp = 32.dp
) {
val icon = when {
isPositive -> Icons.Filled.Check
isClose -> Icons.Filled.Timeline
else -> Icons.Filled.Close
}
val tint = when {
isPositive -> CloserPalette.Evergreen
isClose -> CloserPalette.Gold
else -> CloserPalette.Danger
}
val container = when {
isPositive -> CloserPalette.Evergreen.copy(alpha = 0.12f)
isClose -> CloserPalette.Gold.copy(alpha = 0.12f)
else -> CloserPalette.Danger.copy(alpha = 0.12f)
}
StatusGlyph(
icon = icon,
modifier = modifier,
tint = tint,
container = container,
size = size,
iconSize = (size.value * 0.58f).dp
)
}
@Composable
fun categoryGlyphStyle(
categoryId: String,
iconName: String? = null,
locked: Boolean = false
): GlyphStyle {
if (locked) {
return GlyphStyle(
icon = Icons.Filled.Lock,
tint = MaterialTheme.colorScheme.outline,
container = CloserPalette.PurpleMist
)
}
val key = (iconName ?: categoryId).lowercase().trim()
return when (key) {
"communication", "chat", "question", "questions" -> GlyphStyle(
icon = Icons.AutoMirrored.Filled.Chat,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
"emotional_intimacy", "intimacy", "heart" -> GlyphStyle(
icon = Icons.Filled.Favorite,
tint = CloserPalette.PinkAccentDeep,
container = CloserPalette.PinkMist
)
"physical_intimacy", "sex_and_desire", "sexual_preferences", "desire", "sex" -> GlyphStyle(
icon = Icons.Filled.FavoriteBorder,
tint = CloserPalette.Romantic,
container = CloserPalette.Romantic.copy(alpha = 0.12f)
)
"trust", "rebuilding_trust", "privacy" -> GlyphStyle(
icon = Icons.Filled.Shield,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleGlow
)
"conflict", "conflict_repair", "repair" -> GlyphStyle(
icon = Icons.Filled.Sync,
tint = CloserPalette.PurpleRich,
container = CloserPalette.PurpleMist
)
"future", "timeline" -> GlyphStyle(
icon = Icons.Filled.Timeline,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleSoft
)
"money", "payments" -> GlyphStyle(
icon = Icons.Filled.AttachMoney,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
"home_life", "home" -> GlyphStyle(
icon = Icons.Filled.Home,
tint = CloserPalette.PurpleRich,
container = CloserPalette.PurpleGlow
)
"gratitude", "star" -> GlyphStyle(
icon = Icons.Filled.Star,
tint = CloserPalette.PinkAccentDeep,
container = CloserPalette.PinkMist
)
"parenting", "family", "people" -> GlyphStyle(
icon = Icons.Filled.People,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleSoft
)
"date_night", "date", "calendar" -> GlyphStyle(
icon = Icons.Filled.CalendarToday,
tint = CloserPalette.PinkAccentDeep,
container = CloserPalette.PinkMist
)
"fun", "play" -> GlyphStyle(
icon = Icons.Filled.PlayArrow,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleGlow
)
"stress", "difficult_conversations", "boundaries" -> GlyphStyle(
icon = Icons.Filled.Warning,
tint = CloserPalette.Danger,
container = CloserPalette.Danger.copy(alpha = 0.10f)
)
"values", "marriage" -> GlyphStyle(
icon = Icons.Filled.QuestionAnswer,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
"profile", "person" -> GlyphStyle(
icon = Icons.Filled.Person,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
"reveal", "visibility" -> GlyphStyle(
icon = Icons.Filled.Visibility,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleGlow
)
"predict", "psychology" -> GlyphStyle(
icon = Icons.Filled.Psychology,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleGlow
)
else -> GlyphStyle(
icon = Icons.Filled.Star,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
}
}

View File

@ -20,6 +20,11 @@ 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.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
@ -41,7 +46,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@ -49,6 +53,7 @@ import app.closer.core.navigation.AppRoute
import app.closer.domain.model.ChoiceAnswerConfigImpl
import app.closer.domain.model.Question
import app.closer.domain.repository.QuestionRepository
import app.closer.ui.components.StatusGlyph
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush
import dagger.hilt.android.lifecycle.HiltViewModel
@ -305,10 +310,10 @@ private fun DSIntroScreen(playerNumber: Int, total: Int, onReady: () -> Unit) {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = if (playerNumber == 1) "🔥" else "💜",
fontSize = 56.sp,
textAlign = TextAlign.Center
StatusGlyph(
icon = if (playerNumber == 1) Icons.Filled.FavoriteBorder else Icons.Filled.Visibility,
tint = CloserPalette.Romantic,
container = CloserPalette.Romantic.copy(alpha = 0.12f)
)
Spacer(Modifier.height(20.dp))
Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.Romantic.copy(alpha = 0.14f)) {
@ -358,7 +363,11 @@ private fun DSHandoffScreen(onReady: () -> Unit) {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("🤝", fontSize = 56.sp, textAlign = TextAlign.Center)
StatusGlyph(
icon = Icons.Filled.Sync,
tint = CloserPalette.Romantic,
container = CloserPalette.Romantic.copy(alpha = 0.12f)
)
Spacer(Modifier.height(20.dp))
Text(
text = "Pass the phone!",
@ -510,10 +519,12 @@ private fun DSRevealScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
text = if (matches.isEmpty()) "🤍" else "🔥",
fontSize = 56.sp,
textAlign = TextAlign.Center
StatusGlyph(
icon = if (matches.isEmpty()) Icons.Filled.FavoriteBorder else Icons.Filled.Favorite,
tint = CloserPalette.Romantic,
container = CloserPalette.Romantic.copy(alpha = 0.12f),
size = 82.dp,
iconSize = 40.dp
)
Text(
text = if (matches.isEmpty()) "Nothing in common this round" else "${matches.size} shared desire${if (matches.size != 1) "s" else ""}",
@ -599,7 +610,13 @@ private fun DesireMatchCard(match: DesireMatch) {
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text("❤️", fontSize = 20.sp)
StatusGlyph(
icon = Icons.Filled.Favorite,
tint = CloserPalette.Romantic,
container = CloserPalette.Romantic.copy(alpha = 0.12f),
size = 34.dp,
iconSize = 18.dp
)
Text(
text = match.femaleQ.text,
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium),

View File

@ -43,6 +43,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.questions.displayCategoryName
@Composable
@ -566,6 +567,12 @@ private fun CategoryMiniCard(
modifier = Modifier.padding(15.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CategoryGlyph(
categoryId = item.category.id,
iconName = item.category.iconName,
size = 42.dp,
iconSize = 20.dp
)
Text(
text = item.category.displayName.ifBlank { item.category.id.displayCategoryName() },
style = MaterialTheme.typography.titleSmall,

View File

@ -19,6 +19,11 @@ 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.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Psychology
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.Timeline
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
@ -40,7 +45,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@ -52,6 +56,8 @@ import app.closer.domain.model.Question
import app.closer.domain.model.ScaleAnswerConfigImpl
import app.closer.domain.model.ThisOrThatAnswerConfigImpl
import app.closer.domain.repository.QuestionRepository
import app.closer.ui.components.ResultGlyph
import app.closer.ui.components.StatusGlyph
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush
import dagger.hilt.android.lifecycle.HiltViewModel
@ -306,7 +312,11 @@ private fun PlayerIntroScreen(playerNumber: Int, total: Int, onReady: () -> Unit
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = if (playerNumber == 1) "👤" else "🔮", fontSize = 56.sp, textAlign = TextAlign.Center)
StatusGlyph(
icon = if (playerNumber == 1) Icons.Filled.Person else Icons.Filled.Psychology,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
Spacer(Modifier.height(20.dp))
Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.PurpleMist) {
Text(
@ -358,7 +368,11 @@ private fun HandoffScreen(onReady: () -> Unit) {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("🎉", fontSize = 56.sp, textAlign = TextAlign.Center)
StatusGlyph(
icon = Icons.Filled.Sync,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist
)
Spacer(Modifier.height(20.dp))
Text(
text = "Pass the phone!",
@ -566,10 +580,10 @@ private fun RevealScreen(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (result.isMatch) "" else if (result.isClose) "" else "",
fontSize = 28.sp,
color = accentColor
ResultGlyph(
isPositive = result.isMatch,
isClose = result.isClose,
size = 38.dp
)
Text(
text = if (result.isMatch) "Match!" else if (result.isClose) "So close!" else "Not quite",
@ -678,7 +692,13 @@ private fun CompleteScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text("🎯", fontSize = 56.sp, textAlign = TextAlign.Center)
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),
@ -743,10 +763,10 @@ private fun BreakdownRow(result: HowWellResult) {
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (result.isMatch) "" else if (result.isClose) "" else "",
fontSize = 18.sp,
color = if (result.isMatch) matchColor else if (result.isClose) closeColor else missColor
ResultGlyph(
isPositive = result.isMatch,
isClose = result.isClose,
size = 28.dp
)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(
@ -910,9 +930,9 @@ private fun ScaleInput(
// ── Helpers ───────────────────────────────────────────────────────────────────
private fun scoreLabel(score: Int, total: Int): String = when {
score == total -> "Mind reader! 🤯"
score >= total * 0.8 -> "You really know each other 💜"
score == total -> "Perfect read"
score >= total * 0.8 -> "You really know each other"
score >= total * 0.6 -> "Pretty good!"
score >= total * 0.4 -> "Getting there!"
else -> "Room to grow 🌱"
else -> "Room to grow"
}

View File

@ -17,6 +17,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
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
@ -44,6 +45,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.ui.components.StatusGlyph
import app.closer.ui.settings.SettingsBackgroundBrush
import app.closer.ui.settings.SettingsInk
import app.closer.ui.settings.SettingsMuted
@ -109,10 +111,10 @@ fun InviteConfirmScreen(
} else {
Spacer(Modifier.height(24.dp))
Text(
"",
style = MaterialTheme.typography.displayMedium,
color = SettingsPrimaryDeep
StatusGlyph(
icon = Icons.Filled.FavoriteBorder,
tint = SettingsPrimaryDeep,
container = SettingsSoft
)
Spacer(Modifier.height(16.dp))

View File

@ -19,6 +19,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.Button
@ -39,6 +40,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import app.closer.core.navigation.AppRoute
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush
import app.closer.ui.theme.closerBrandGlyphBrush
@ -261,15 +263,12 @@ private fun DesireSyncCard(
horizontalArrangement = Arrangement.spacedBy(14.dp),
verticalAlignment = Alignment.CenterVertically
) {
Surface(
shape = RoundedCornerShape(18.dp),
color = CloserPalette.Romantic.copy(alpha = 0.12f),
modifier = Modifier.size(52.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(text = "🔥", style = MaterialTheme.typography.titleMedium)
}
}
CategoryGlyph(
categoryId = "sex_and_desire",
modifier = Modifier.size(52.dp),
size = 52.dp,
iconSize = 25.dp
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp)
@ -290,13 +289,24 @@ private fun DesireSyncCard(
shape = RoundedCornerShape(999.dp),
color = CloserPalette.Romantic.copy(alpha = 0.12f)
) {
Text(
text = "🔒 Premium",
Row(
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = CloserPalette.Romantic,
fontWeight = FontWeight.SemiBold
)
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Filled.Lock,
contentDescription = null,
tint = CloserPalette.Romantic,
modifier = Modifier.size(13.dp)
)
Text(
text = "Premium",
style = MaterialTheme.typography.labelSmall,
color = CloserPalette.Romantic,
fontWeight = FontWeight.SemiBold
)
}
}
}
Text(
@ -335,18 +345,12 @@ private fun HowWellCard(
horizontalArrangement = Arrangement.spacedBy(14.dp),
verticalAlignment = Alignment.CenterVertically
) {
Surface(
shape = RoundedCornerShape(18.dp),
color = CloserPalette.Romantic.copy(alpha = 0.12f),
modifier = Modifier.size(52.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(
text = "💜",
style = MaterialTheme.typography.titleMedium
)
}
}
CategoryGlyph(
categoryId = "predict",
modifier = Modifier.size(52.dp),
size = 52.dp,
iconSize = 25.dp
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp)

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -42,6 +43,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
@Composable
fun QuestionCategoryScreen(
@ -168,21 +170,39 @@ private fun CategoryHero(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.SemiBold),
color = Color(0xFF261D2E),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
Text(
text = category?.description
?: "Browse prompts for this kind of conversation.",
style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF5A5060),
maxLines = 3,
overflow = TextOverflow.Ellipsis
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(14.dp),
verticalAlignment = Alignment.Top
) {
CategoryGlyph(
categoryId = category?.id ?: title,
iconName = category?.iconName,
modifier = Modifier.size(58.dp),
size = 58.dp,
iconSize = 28.dp
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.SemiBold),
color = Color(0xFF261D2E),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
Text(
text = category?.description
?: "Browse prompts for this kind of conversation.",
style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF5A5060),
maxLines = 3,
overflow = TextOverflow.Ellipsis
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -44,6 +45,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
private enum class PackFilter(val label: String) {
ALL("All"),
@ -226,6 +228,13 @@ private fun QuestionPackCard(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.Top
) {
CategoryGlyph(
categoryId = item.category.id,
iconName = item.category.iconName,
locked = item.isLocked,
modifier = Modifier.size(50.dp),
size = 50.dp
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(6.dp)

View File

@ -17,9 +17,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
@ -41,6 +39,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.questions.displayCategoryName
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush
@ -257,20 +256,13 @@ private fun CategoryCard(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Surface(
shape = RoundedCornerShape(18.dp),
color = if (item.isLocked) CloserPalette.PurpleMist else MaterialTheme.colorScheme.secondaryContainer,
modifier = Modifier.size(48.dp)
) {
Box(contentAlignment = Alignment.Center) {
Icon(
imageVector = if (item.isLocked) Icons.Default.Lock else Icons.Filled.Star,
contentDescription = null,
tint = if (item.isLocked) MaterialTheme.colorScheme.outline else CloserPalette.PinkAccentDeep,
modifier = Modifier.size(22.dp)
)
}
}
CategoryGlyph(
categoryId = item.category.id,
iconName = item.category.iconName,
locked = item.isLocked,
modifier = Modifier.size(48.dp),
size = 48.dp
)
Column(
modifier = Modifier
.weight(1f)

View File

@ -21,6 +21,8 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
@ -39,8 +41,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.ui.components.StatusGlyph
import app.closer.ui.theme.CloserPalette
@HiltViewModel
class WheelCompleteViewModel @Inject constructor(
@ -120,10 +123,12 @@ private fun WheelCompleteContent(
verticalArrangement = Arrangement.spacedBy(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "",
fontSize = 64.sp,
color = Color(0xFFB98AF4)
StatusGlyph(
icon = Icons.Filled.Check,
tint = CloserPalette.PurpleDeep,
container = CloserPalette.PurpleMist,
size = 82.dp,
iconSize = 40.dp
)
Column(