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(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background 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.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.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth 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.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.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
@ -30,6 +33,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
@ -41,6 +45,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.domain.model.LocalAnswer import app.closer.domain.model.LocalAnswer
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.components.EmptyState import app.closer.ui.components.EmptyState
import app.closer.ui.questions.displayCategoryName import app.closer.ui.questions.displayCategoryName
@ -157,6 +162,7 @@ private fun AnswerHistoryContent(
} }
@OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
private fun AnswerHistoryCard( private fun AnswerHistoryCard(
answer: LocalAnswer, answer: LocalAnswer,
@ -170,11 +176,25 @@ private fun AnswerHistoryCard(
colors = CardDefaults.cardColors(containerColor = Color.White), colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
) { ) {
Column( Row(
modifier = Modifier.padding(17.dp), modifier = Modifier.padding(17.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.Top
) {
CategoryGlyph(
categoryId = answer.category,
modifier = Modifier.size(46.dp),
size = 46.dp,
iconSize = 22.dp
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
HistoryPill(if (answer.isRevealed) "Revealed" else "Private") HistoryPill(if (answer.isRevealed) "Revealed" else "Private")
HistoryPill(answer.category.displayCategoryName()) HistoryPill(answer.category.displayCategoryName())
} }
@ -215,6 +235,7 @@ private fun AnswerHistoryCard(
} }
} }
} }
}
} }
@Composable @Composable

View File

@ -17,6 +17,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@ -47,6 +48,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.ui.components.StatusGlyph
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -97,7 +99,11 @@ fun ForgotPasswordScreen(
if (state.sent) { if (state.sent) {
Spacer(Modifier.height(48.dp)) 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)) Spacer(Modifier.height(16.dp))
Text( Text(
"Reset email sent", "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.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.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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card 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.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.ChoiceAnswerConfigImpl
import app.closer.domain.model.Question import app.closer.domain.model.Question
import app.closer.domain.repository.QuestionRepository import app.closer.domain.repository.QuestionRepository
import app.closer.ui.components.StatusGlyph
import app.closer.ui.theme.CloserPalette import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -305,10 +310,10 @@ private fun DSIntroScreen(playerNumber: Int, total: Int, onReady: () -> Unit) {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( StatusGlyph(
text = if (playerNumber == 1) "🔥" else "💜", icon = if (playerNumber == 1) Icons.Filled.FavoriteBorder else Icons.Filled.Visibility,
fontSize = 56.sp, tint = CloserPalette.Romantic,
textAlign = TextAlign.Center container = CloserPalette.Romantic.copy(alpha = 0.12f)
) )
Spacer(Modifier.height(20.dp)) Spacer(Modifier.height(20.dp))
Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.Romantic.copy(alpha = 0.14f)) { Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.Romantic.copy(alpha = 0.14f)) {
@ -358,7 +363,11 @@ private fun DSHandoffScreen(onReady: () -> Unit) {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally 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)) Spacer(Modifier.height(20.dp))
Text( Text(
text = "Pass the phone!", text = "Pass the phone!",
@ -510,10 +519,12 @@ private fun DSRevealScreen(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp) verticalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
Text( StatusGlyph(
text = if (matches.isEmpty()) "🤍" else "🔥", icon = if (matches.isEmpty()) Icons.Filled.FavoriteBorder else Icons.Filled.Favorite,
fontSize = 56.sp, tint = CloserPalette.Romantic,
textAlign = TextAlign.Center container = CloserPalette.Romantic.copy(alpha = 0.12f),
size = 82.dp,
iconSize = 40.dp
) )
Text( Text(
text = if (matches.isEmpty()) "Nothing in common this round" else "${matches.size} shared desire${if (matches.size != 1) "s" else ""}", 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), horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically 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(
text = match.femaleQ.text, text = match.femaleQ.text,
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium), 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.core.navigation.AppRoute
import app.closer.domain.model.Question import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.questions.displayCategoryName import app.closer.ui.questions.displayCategoryName
@Composable @Composable
@ -566,6 +567,12 @@ private fun CategoryMiniCard(
modifier = Modifier.padding(15.dp), modifier = Modifier.padding(15.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
CategoryGlyph(
categoryId = item.category.id,
iconName = item.category.iconName,
size = 42.dp,
iconSize = 20.dp
)
Text( Text(
text = item.category.displayName.ifBlank { item.category.id.displayCategoryName() }, text = item.category.displayName.ifBlank { item.category.id.displayCategoryName() },
style = MaterialTheme.typography.titleSmall, 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.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.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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card 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.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.ScaleAnswerConfigImpl
import app.closer.domain.model.ThisOrThatAnswerConfigImpl import app.closer.domain.model.ThisOrThatAnswerConfigImpl
import app.closer.domain.repository.QuestionRepository 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.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -306,7 +312,11 @@ private fun PlayerIntroScreen(playerNumber: Int, total: Int, onReady: () -> Unit
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally 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)) Spacer(Modifier.height(20.dp))
Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.PurpleMist) { Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.PurpleMist) {
Text( Text(
@ -358,7 +368,11 @@ private fun HandoffScreen(onReady: () -> Unit) {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally 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)) Spacer(Modifier.height(20.dp))
Text( Text(
text = "Pass the phone!", text = "Pass the phone!",
@ -566,10 +580,10 @@ private fun RevealScreen(
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( ResultGlyph(
text = if (result.isMatch) "" else if (result.isClose) "" else "", isPositive = result.isMatch,
fontSize = 28.sp, isClose = result.isClose,
color = accentColor size = 38.dp
) )
Text( Text(
text = if (result.isMatch) "Match!" else if (result.isClose) "So close!" else "Not quite", text = if (result.isMatch) "Match!" else if (result.isClose) "So close!" else "Not quite",
@ -678,7 +692,13 @@ private fun CompleteScreen(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp) 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(
text = "$score / $total", text = "$score / $total",
style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold), style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold),
@ -743,10 +763,10 @@ private fun BreakdownRow(result: HowWellResult) {
horizontalArrangement = Arrangement.spacedBy(10.dp), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( ResultGlyph(
text = if (result.isMatch) "" else if (result.isClose) "" else "", isPositive = result.isMatch,
fontSize = 18.sp, isClose = result.isClose,
color = if (result.isMatch) matchColor else if (result.isClose) closeColor else missColor size = 28.dp
) )
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text( Text(
@ -910,9 +930,9 @@ private fun ScaleInput(
// ── Helpers ─────────────────────────────────────────────────────────────────── // ── Helpers ───────────────────────────────────────────────────────────────────
private fun scoreLabel(score: Int, total: Int): String = when { private fun scoreLabel(score: Int, total: Int): String = when {
score == total -> "Mind reader! 🤯" score == total -> "Perfect read"
score >= total * 0.8 -> "You really know each other 💜" score >= total * 0.8 -> "You really know each other"
score >= total * 0.6 -> "Pretty good!" score >= total * 0.6 -> "Pretty good!"
score >= total * 0.4 -> "Getting there!" 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.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@ -44,6 +45,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.ui.components.StatusGlyph
import app.closer.ui.settings.SettingsBackgroundBrush import app.closer.ui.settings.SettingsBackgroundBrush
import app.closer.ui.settings.SettingsInk import app.closer.ui.settings.SettingsInk
import app.closer.ui.settings.SettingsMuted import app.closer.ui.settings.SettingsMuted
@ -109,10 +111,10 @@ fun InviteConfirmScreen(
} else { } else {
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(24.dp))
Text( StatusGlyph(
"", icon = Icons.Filled.FavoriteBorder,
style = MaterialTheme.typography.displayMedium, tint = SettingsPrimaryDeep,
color = SettingsPrimaryDeep container = SettingsSoft
) )
Spacer(Modifier.height(16.dp)) 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.Done
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home 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.PlayArrow
import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.Button 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.theme.CloserPalette import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
import app.closer.ui.theme.closerBrandGlyphBrush import app.closer.ui.theme.closerBrandGlyphBrush
@ -261,15 +263,12 @@ private fun DesireSyncCard(
horizontalArrangement = Arrangement.spacedBy(14.dp), horizontalArrangement = Arrangement.spacedBy(14.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( CategoryGlyph(
shape = RoundedCornerShape(18.dp), categoryId = "sex_and_desire",
color = CloserPalette.Romantic.copy(alpha = 0.12f), modifier = Modifier.size(52.dp),
modifier = Modifier.size(52.dp) size = 52.dp,
) { iconSize = 25.dp
Box(contentAlignment = Alignment.Center) { )
Text(text = "🔥", style = MaterialTheme.typography.titleMedium)
}
}
Column( Column(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
@ -290,15 +289,26 @@ private fun DesireSyncCard(
shape = RoundedCornerShape(999.dp), shape = RoundedCornerShape(999.dp),
color = CloserPalette.Romantic.copy(alpha = 0.12f) color = CloserPalette.Romantic.copy(alpha = 0.12f)
) { ) {
Text( Row(
text = "🔒 Premium",
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp), modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
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, style = MaterialTheme.typography.labelSmall,
color = CloserPalette.Romantic, color = CloserPalette.Romantic,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )
} }
} }
}
Text( Text(
text = "Both answer privately. Only shared desires are revealed.", text = "Both answer privately. Only shared desires are revealed.",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
@ -335,18 +345,12 @@ private fun HowWellCard(
horizontalArrangement = Arrangement.spacedBy(14.dp), horizontalArrangement = Arrangement.spacedBy(14.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( CategoryGlyph(
shape = RoundedCornerShape(18.dp), categoryId = "predict",
color = CloserPalette.Romantic.copy(alpha = 0.12f), modifier = Modifier.size(52.dp),
modifier = Modifier.size(52.dp) size = 52.dp,
) { iconSize = 25.dp
Box(contentAlignment = Alignment.Center) {
Text(
text = "💜",
style = MaterialTheme.typography.titleMedium
) )
}
}
Column( Column(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp) 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.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items 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.core.navigation.AppRoute
import app.closer.domain.model.Question import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
@Composable @Composable
fun QuestionCategoryScreen( fun QuestionCategoryScreen(
@ -167,6 +169,22 @@ private fun CategoryHero(
Column( Column(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(12.dp)
) {
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(
text = title, text = title,
@ -183,6 +201,8 @@ private fun CategoryHero(
maxLines = 3, maxLines = 3,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
}
}
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -44,6 +45,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.domain.model.QuestionCategory import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
private enum class PackFilter(val label: String) { private enum class PackFilter(val label: String) {
ALL("All"), ALL("All"),
@ -226,6 +228,13 @@ private fun QuestionPackCard(
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.Top verticalAlignment = Alignment.Top
) { ) {
CategoryGlyph(
categoryId = item.category.id,
iconName = item.category.iconName,
locked = item.isLocked,
modifier = Modifier.size(50.dp),
size = 50.dp
)
Column( Column(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(6.dp) 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.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.Icons 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.PlayArrow
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
@ -41,6 +39,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.domain.model.QuestionCategory import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.questions.displayCategoryName import app.closer.ui.questions.displayCategoryName
import app.closer.ui.theme.CloserPalette import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
@ -257,20 +256,13 @@ private fun CategoryCard(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( CategoryGlyph(
shape = RoundedCornerShape(18.dp), categoryId = item.category.id,
color = if (item.isLocked) CloserPalette.PurpleMist else MaterialTheme.colorScheme.secondaryContainer, iconName = item.category.iconName,
modifier = Modifier.size(48.dp) locked = item.isLocked,
) { modifier = Modifier.size(48.dp),
Box(contentAlignment = Alignment.Center) { size = 48.dp
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)
) )
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .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.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.shape.RoundedCornerShape 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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card 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.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import app.closer.ui.components.StatusGlyph
import app.closer.ui.theme.CloserPalette
@HiltViewModel @HiltViewModel
class WheelCompleteViewModel @Inject constructor( class WheelCompleteViewModel @Inject constructor(
@ -120,10 +123,12 @@ private fun WheelCompleteContent(
verticalArrangement = Arrangement.spacedBy(24.dp), verticalArrangement = Arrangement.spacedBy(24.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( StatusGlyph(
text = "", icon = Icons.Filled.Check,
fontSize = 64.sp, tint = CloserPalette.PurpleDeep,
color = Color(0xFFB98AF4) container = CloserPalette.PurpleMist,
size = 82.dp,
iconSize = 40.dp
) )
Column( Column(