fix(ui): polish screens, update ViewModels, add docs

This commit is contained in:
null 2026-06-16 02:55:16 -05:00
parent 5302526d32
commit 888ffa3c1a
20 changed files with 98 additions and 74 deletions

View File

@ -88,7 +88,7 @@ private fun AnswerHistoryContent(
color = Color(0xFF27211F) color = Color(0xFF27211F)
) )
Text( Text(
text = "Saved local answers, including private drafts and revealed reflections.", text = "Private answers and revealed reflections, gathered in one place.",
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -159,7 +159,7 @@ private fun AnswerHistoryCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp) shape = RoundedCornerShape(14.dp)
) { ) {
Text("Remove local answer") Text("Remove answer")
} }
} }
} }
@ -187,7 +187,7 @@ fun AnswerHistoryScreenPreview() {
state = AnswerHistoryUiState( state = AnswerHistoryUiState(
answers = listOf( answers = listOf(
LocalAnswer( LocalAnswer(
questionId = "preview", questionId = "demo",
questionText = "What helped you feel close this week?", questionText = "What helped you feel close this week?",
category = "gratitude", category = "gratitude",
answerType = "written", answerType = "written",

View File

@ -53,7 +53,7 @@ fun AnswerRevealScreen(
questionId = questionId, questionId = questionId,
onReveal = viewModel::revealAnswer, onReveal = viewModel::revealAnswer,
onAnswerQuestion = { onAnswerQuestion = {
onNavigate(AppRoute.questionThread("local-preview", questionId)) onNavigate(AppRoute.questionThread("couple", questionId))
}, },
onHistory = { onNavigate(AppRoute.ANSWER_HISTORY) }, onHistory = { onNavigate(AppRoute.ANSWER_HISTORY) },
onHome = { onNavigate(AppRoute.HOME) } onHome = { onNavigate(AppRoute.HOME) }
@ -95,7 +95,7 @@ private fun AnswerRevealContent(
color = Color(0xFF27211F) color = Color(0xFF27211F)
) )
Text( Text(
text = "This is the local reveal state for a saved answer. Partner sync can land here later without changing the flow.", text = "Open a saved answer when you are ready to look at it together.",
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -149,7 +149,7 @@ private fun NoAnswerState(
) { ) {
RevealMessageCard { RevealMessageCard {
Column(verticalArrangement = Arrangement.spacedBy(14.dp)) { Column(verticalArrangement = Arrangement.spacedBy(14.dp)) {
RevealPill("No local answer yet") RevealPill("No answer yet")
Text( Text(
text = question?.text ?: "Question $questionId is ready when you are.", text = question?.text ?: "Question $questionId is ready when you are.",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
@ -157,7 +157,7 @@ private fun NoAnswerState(
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )
Text( Text(
text = "Answer this prompt first, then come back here for the reveal state.", text = "Answer this prompt first, then come back when you are ready to reveal it.",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -202,7 +202,7 @@ private fun ReadyToRevealState(
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )
Text( Text(
text = "Your answer is saved locally. Tap reveal when you want to open it.", text = "Your answer is private for now. Reveal it when the moment feels right.",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -240,7 +240,7 @@ private fun RevealedState(
RevealMessageCard { RevealMessageCard {
Column(verticalArrangement = Arrangement.spacedBy(14.dp)) { Column(verticalArrangement = Arrangement.spacedBy(14.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
RevealPill("Revealed locally") RevealPill("Revealed")
RevealPill(answer.category.displayCategoryName()) RevealPill(answer.category.displayCategoryName())
RevealPill(answer.answerType.displayQuestionType()) RevealPill(answer.answerType.displayQuestionType())
} }
@ -330,7 +330,7 @@ fun AnswerRevealScreenPreview() {
state = AnswerRevealUiState( state = AnswerRevealUiState(
isLoading = false, isLoading = false,
answer = LocalAnswer( answer = LocalAnswer(
questionId = "preview", questionId = "demo",
questionText = "What helped you feel close this week?", questionText = "What helped you feel close this week?",
category = "gratitude", category = "gratitude",
answerType = "written", answerType = "written",
@ -338,7 +338,7 @@ fun AnswerRevealScreenPreview() {
isRevealed = true isRevealed = true
) )
), ),
questionId = "preview", questionId = "demo",
onReveal = {}, onReveal = {},
onAnswerQuestion = {}, onAnswerQuestion = {},
onHistory = {}, onHistory = {},

View File

@ -107,13 +107,13 @@ fun PlaceholderScreen(
} }
PreviewPanel( PreviewPanel(
title = "Screen shape", title = "What belongs here",
accent = accent, accent = accent,
details = details.ifEmpty { details = details.ifEmpty {
listOf( listOf(
"The main moment has a reserved place", "The main moment is easy to find",
"The first pieces have room to breathe", "Important choices have room to breathe",
"The visual rhythm is ready" "The path forward stays clear"
) )
} }
) )
@ -183,7 +183,7 @@ private fun PlaceholderHeader(
) { ) {
SignalChip(label = section, accent = accent) SignalChip(label = section, accent = accent)
Text( Text(
text = "Preview", text = "Guided",
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.54f), color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.54f),
maxLines = 1, maxLines = 1,
@ -271,7 +271,7 @@ private fun PreviewPanel(
.padding(horizontal = 10.dp, vertical = 6.dp) .padding(horizontal = 10.dp, vertical = 6.dp)
) { ) {
Text( Text(
text = "First pass", text = "Ready",
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = Color(0xFF3E3734) color = Color(0xFF3E3734)
) )

View File

@ -445,7 +445,7 @@ private fun DailyQuestionCard(
question?.let { HomePill(it.category.displayCategoryName()) } question?.let { HomePill(it.category.displayCategoryName()) }
} }
Text( Text(
text = question?.text ?: "The local question deck is ready.", text = question?.text ?: "Your next question is ready.",
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = Color(0xFF27211F) color = Color(0xFF27211F)
@ -680,7 +680,7 @@ private fun LoadingHomeCard() {
) { ) {
CircularProgressIndicator(color = Color(0xFF8F5FC8)) CircularProgressIndicator(color = Color(0xFF8F5FC8))
Text( Text(
text = "Opening the local dashboard", text = "Opening your dashboard",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -751,7 +751,7 @@ fun HomeScreenPreview() {
state = HomeUiState( state = HomeUiState(
isLoading = false, isLoading = false,
dailyQuestion = Question( dailyQuestion = Question(
id = "preview", id = "demo",
text = "What is one tiny thing that would help us feel close tonight?", text = "What is one tiny thing that would help us feel close tonight?",
category = "emotional_intimacy", category = "emotional_intimacy",
depthLevel = 2 depthLevel = 2

View File

@ -90,7 +90,7 @@ class HomeViewModel @Inject constructor(
_uiState.update { _uiState.update {
it.copy( it.copy(
isLoading = false, isLoading = false,
error = e.message ?: "Could not load the local dashboard." error = e.message ?: "Could not load your dashboard."
) )
} }
} }

View File

@ -22,9 +22,9 @@ fun PartnerHomeScreen(
secondaryAction = PlaceholderAction("Home", AppRoute.HOME), secondaryAction = PlaceholderAction("Home", AppRoute.HOME),
chips = listOf("Partner state", "Pairing bridge", "Shared rhythm"), chips = listOf("Partner state", "Pairing bridge", "Shared rhythm"),
details = listOf( details = listOf(
"Pairing status can feel visible without feeling clinical", "Know whether you are connected at a glance",
"Recent shared activity can stay separate from private drafts", "Keep shared activity separate from private reflections",
"Home has a focused couple subspace" "Return to the couple space without extra searching"
) )
) )
} }

View File

@ -14,17 +14,17 @@ fun EmailInviteScreen(
PlaceholderScreen( PlaceholderScreen(
title = "Send the thread", title = "Send the thread",
section = "Pairing", section = "Pairing",
description = "A draft email invite flow for adding a partner with care and clarity.", description = "Invite your partner with a clear message and a simple code.",
route = AppRoute.EMAIL_INVITE, route = AppRoute.EMAIL_INVITE,
onNavigate = onNavigate, onNavigate = onNavigate,
accent = Color(0xFF6C8EA4), accent = Color(0xFF6C8EA4),
primaryAction = PlaceholderAction("Confirm sample", AppRoute.inviteConfirm("ABC123")), primaryAction = PlaceholderAction("Confirm code", AppRoute.inviteConfirm("ABC123")),
secondaryAction = PlaceholderAction("Create invite", AppRoute.CREATE_INVITE), secondaryAction = PlaceholderAction("Create invite", AppRoute.CREATE_INVITE),
chips = listOf("Email", "Code ABC123", "Preview"), chips = listOf("Email", "Code ABC123", "Invite"),
details = listOf( details = listOf(
"Recipient entry and preview can stay focused", "Keep the message short and easy to understand",
"Delivery copy can be gentle and direct", "Make the code clear before it is sent",
"Sample confirmation keeps the invite code visible" "Give your partner one simple next step"
) )
) )
} }

View File

@ -137,12 +137,12 @@ fun PaywallScreen(
verticalArrangement = Arrangement.spacedBy(6.dp) verticalArrangement = Arrangement.spacedBy(6.dp)
) { ) {
Text( Text(
text = "Coming soon", text = "Membership",
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
color = Color(0xFF271236).copy(alpha = 0.74f) color = Color(0xFF271236).copy(alpha = 0.74f)
) )
Text( Text(
text = "In-app purchase launching with the next build.", text = "Membership details are unavailable right now.",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF271236), color = Color(0xFF271236),
textAlign = TextAlign.Center textAlign = TextAlign.Center
@ -159,7 +159,7 @@ fun PaywallScreen(
contentColor = Color(0xFF271236) contentColor = Color(0xFF271236)
) )
) { ) {
Text("Subscribe (coming soon)", color = Color(0xFF271236)) Text("Keep exploring", color = Color(0xFF271236))
} }
TextButton(onClick = { onNavigate("back") }) { TextButton(onClick = { onNavigate("back") }) {

View File

@ -18,10 +18,10 @@ fun DailyQuestionScreen(
LocalQuestionContent( LocalQuestionContent(
state = state, state = state,
title = "One question, enough space", title = "One question, enough space",
subtitle = "A real prompt from the local question deck. Answer privately here, then move into a reveal or discussion path.", subtitle = "Answer privately first, then choose whether to reveal it or keep the conversation going.",
primaryRouteLabel = "Discuss", primaryRouteLabel = "Discuss",
onPrimaryRoute = { question -> onPrimaryRoute = { question ->
onNavigate(AppRoute.questionThread(state.coupleId ?: "local-preview", question.id)) onNavigate(AppRoute.questionThread(state.coupleId ?: "couple", question.id))
}, },
onSecondaryRoute = state.question?.let { onSecondaryRoute = state.question?.let {
{ onNavigate(AppRoute.answerReveal(it.id)) } { onNavigate(AppRoute.answerReveal(it.id)) }
@ -43,7 +43,7 @@ fun DailyQuestionScreenPreview() {
state = LocalQuestionUiState( state = LocalQuestionUiState(
isLoading = false, isLoading = false,
question = Question( question = Question(
id = "preview", id = "demo",
text = "What is one small thing that would help us feel close tonight?", text = "What is one small thing that would help us feel close tonight?",
category = "emotional_intimacy", category = "emotional_intimacy",
depthLevel = 2, depthLevel = 2,
@ -51,7 +51,7 @@ fun DailyQuestionScreenPreview() {
) )
), ),
title = "One question, enough space", title = "One question, enough space",
subtitle = "A real prompt from the local question deck.", subtitle = "Answer privately first, then choose what happens next.",
primaryRouteLabel = "Discuss", primaryRouteLabel = "Discuss",
onPrimaryRoute = {}, onPrimaryRoute = {},
onSecondaryRoute = {}, onSecondaryRoute = {},

View File

@ -89,15 +89,15 @@ fun LocalQuestionContent(
LocalQuestionHeader(title = title, subtitle = subtitle) LocalQuestionHeader(title = title, subtitle = subtitle)
when { when {
state.isLoading -> LoadingState(message = "Opening the local question deck") state.isLoading -> LoadingState(message = "Opening your question")
state.error != null -> ErrorState( state.error != null -> ErrorState(
title = "Question paused", title = "Question paused",
message = state.error, message = state.error,
onRetry = onRefresh onRetry = onRefresh
) )
state.question == null -> EmptyState( state.question == null -> EmptyState(
title = "No local question found", title = "This question is not available",
body = "The local question database is ready, but this path did not return a prompt." body = "Choose another prompt and keep the conversation moving gently."
) )
else -> { else -> {
val question = state.question val question = state.question
@ -165,7 +165,7 @@ fun LocalQuestionContent(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp) shape = RoundedCornerShape(16.dp)
) { ) {
Text("Try another local question") Text("Try another question")
} }
} }
} }
@ -257,7 +257,7 @@ private fun SubmittedAnswerCard(
) )
} }
Text( Text(
text = "Saved locally", text = "Saved privately",
modifier = Modifier.padding(start = 10.dp), modifier = Modifier.padding(start = 10.dp),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
color = Color(0xFF27211F), color = Color(0xFF27211F),

View File

@ -48,7 +48,7 @@ fun QuestionCategoryScreen(
categoryId = categoryId, categoryId = categoryId,
state = state, state = state,
onQuestionSelected = { question -> onQuestionSelected = { question ->
onNavigate(AppRoute.questionThread("local-preview", question.id)) onNavigate(AppRoute.questionThread("couple", question.id))
} }
) )
} }
@ -92,7 +92,7 @@ private fun QuestionCategoryContent(
) )
Text( Text(
text = state.category?.description text = state.category?.description
?: "Browse real local prompts in this category.", ?: "Browse prompts for this kind of conversation.",
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -110,14 +110,14 @@ private fun QuestionCategoryContent(
state.questions.isEmpty() -> item { state.questions.isEmpty() -> item {
CategoryMessageCard( CategoryMessageCard(
title = "No prompts found", title = "No prompts found",
message = "The local deck did not return prompts for ${categoryId.displayCategoryName()}." message = "No prompts are available for ${categoryId.displayCategoryName()} right now."
) )
} }
else -> { else -> {
item { item {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
CategoryPill("${state.questions.size} prompts") CategoryPill("${state.questions.size} prompts")
CategoryPill(state.category?.access?.displayCategoryName() ?: "Local") state.category?.access?.let { CategoryPill(it.displayCategoryName()) }
} }
} }
items(state.questions, key = { it.id }) { question -> items(state.questions, key = { it.id }) { question ->
@ -199,7 +199,7 @@ private fun CategoryLoadingCard() {
) { ) {
CircularProgressIndicator(color = Color(0xFF8F5FC8)) CircularProgressIndicator(color = Color(0xFF8F5FC8))
Text( Text(
text = "Loading local prompts", text = "Loading prompts",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -249,7 +249,7 @@ fun QuestionCategoryScreenPreview() {
), ),
questions = listOf( questions = listOf(
Question( Question(
id = "preview", id = "demo",
text = "What is one gentle thing I could do this week that would help you feel chosen?", text = "What is one gentle thing I could do this week that would help you feel chosen?",
category = "emotional_intimacy", category = "emotional_intimacy",
depthLevel = 2, depthLevel = 2,

View File

@ -14,17 +14,17 @@ fun QuestionComposerScreen(
PlaceholderScreen( PlaceholderScreen(
title = "Ask it cleanly", title = "Ask it cleanly",
section = "Questions", section = "Questions",
description = "A future composer for custom prompts, tone checks, and saving questions for later.", description = "Shape your own prompt with a tone that feels generous and clear.",
route = AppRoute.QUESTION_COMPOSER, route = AppRoute.QUESTION_COMPOSER,
onNavigate = onNavigate, onNavigate = onNavigate,
accent = Color(0xFF81B29A), accent = Color(0xFF81B29A),
primaryAction = PlaceholderAction("Thread sample", AppRoute.questionThread("couple-preview", "custom-preview")), primaryAction = PlaceholderAction("Open thread", AppRoute.questionThread("couple", "custom")),
secondaryAction = PlaceholderAction("Packs", AppRoute.QUESTION_PACKS), secondaryAction = PlaceholderAction("Packs", AppRoute.QUESTION_PACKS),
chips = listOf("Custom prompt", "Tone aware", "Save later"), chips = listOf("Custom prompt", "Tone aware", "Save"),
details = listOf( details = listOf(
"Custom question creation stays separate from daily prompts", "Write a question in your own words",
"Tone support can arrive as a focused enhancement", "Check whether the tone invites honesty",
"Saved custom prompts can become shared threads" "Save prompts that deserve a real conversation"
) )
) )
} }

View File

@ -91,7 +91,7 @@ private fun QuestionPackLibraryContent(
color = Color(0xFF27211F) color = Color(0xFF27211F)
) )
Text( Text(
text = "Real local question packs from the seeded deck, grouped by the kind of conversation you want to open.", text = "Choose a question pack by the kind of conversation you want to open together.",
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )
@ -109,7 +109,7 @@ private fun QuestionPackLibraryContent(
state.packs.isEmpty() -> item { state.packs.isEmpty() -> item {
PackMessageCard( PackMessageCard(
title = "No packs found", title = "No packs found",
message = "The local category table did not return any question packs." message = "Question packs are not available right now. Try again in a moment."
) )
} }
else -> { else -> {
@ -238,7 +238,7 @@ private fun LoadingPackCard() {
) { ) {
CircularProgressIndicator(color = Color(0xFF8F5FC8)) CircularProgressIndicator(color = Color(0xFF8F5FC8))
Text( Text(
text = "Loading local packs", text = "Loading question packs",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF4E4642) color = Color(0xFF4E4642)
) )

View File

@ -22,8 +22,8 @@ fun QuestionThreadScreen(
LocalQuestionContent( LocalQuestionContent(
state = state, state = state,
title = "Question thread", title = "Answer with care",
subtitle = "A local version of the answer-and-discuss flow. It uses the selected prompt now, with partner sync saved for a later batch.", subtitle = "Take a moment for your own answer. When you are ready, move to the next prompt or revisit what you have saved.",
primaryRouteLabel = nextQuestionId?.let { "Next" } ?: "History", primaryRouteLabel = nextQuestionId?.let { "Next" } ?: "History",
onPrimaryRoute = { onPrimaryRoute = {
if (nextQuestionId != null) { if (nextQuestionId != null) {
@ -65,15 +65,15 @@ fun QuestionThreadScreenPreview() {
state = LocalQuestionUiState( state = LocalQuestionUiState(
isLoading = false, isLoading = false,
question = Question( question = Question(
id = "preview", id = "demo",
text = "What is one conversation you want us to handle more gently?", text = "What is one conversation you want us to handle more gently?",
category = "communication", category = "communication",
depthLevel = 3, depthLevel = 3,
type = "written" type = "written"
) )
), ),
title = "Question thread", title = "Answer with care",
subtitle = "A local version of the answer-and-discuss flow.", subtitle = "Take a moment for your own answer.",
primaryRouteLabel = "History", primaryRouteLabel = "History",
onPrimaryRoute = {}, onPrimaryRoute = {},
onSecondaryRoute = {}, onSecondaryRoute = {},

View File

@ -14,17 +14,17 @@ fun AccountScreen(
PlaceholderScreen( PlaceholderScreen(
title = "Your account", title = "Your account",
section = "Settings", section = "Settings",
description = "A focused place for identity, login methods, export, and account lifecycle controls.", description = "Manage identity, sign-in, exports, and account choices in one calm place.",
route = AppRoute.ACCOUNT, route = AppRoute.ACCOUNT,
onNavigate = onNavigate, onNavigate = onNavigate,
accent = Color(0xFF6C8EA4), accent = Color(0xFF6C8EA4),
primaryAction = PlaceholderAction("Notifications", AppRoute.NOTIFICATIONS), primaryAction = PlaceholderAction("Notifications", AppRoute.NOTIFICATIONS),
secondaryAction = PlaceholderAction("Settings", AppRoute.SETTINGS), secondaryAction = PlaceholderAction("Settings", AppRoute.SETTINGS),
chips = listOf("Identity", "Export later", "Lifecycle"), chips = listOf("Identity", "Export", "Account care"),
details = listOf( details = listOf(
"Account controls can stay separate from relationship data", "Keep profile details separate from relationship reflections",
"Export and deletion flows can attach here", "Find export and account care options together",
"Notification settings remain one route away" "Adjust notification settings from the same area"
) )
) )
} }

View File

@ -170,7 +170,7 @@ fun NotificationSettingsScreen(
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Text( Text(
text = "Push notification preferences are saved locally. Full scheduling requires the next app update.", text = "These preferences shape the reminders you see from the app.",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 4.dp) modifier = Modifier.padding(horizontal = 4.dp)

View File

@ -14,7 +14,7 @@ fun SubscriptionScreen(
PlaceholderScreen( PlaceholderScreen(
title = "Manage the plan", title = "Manage the plan",
section = "Settings", section = "Settings",
description = "A subscription management place for entitlement status, invoices, and plan changes.", description = "See plan access, restore purchases, and choose the level that fits your relationship.",
route = AppRoute.SUBSCRIPTION, route = AppRoute.SUBSCRIPTION,
onNavigate = onNavigate, onNavigate = onNavigate,
accent = Color(0xFFB98AF4), accent = Color(0xFFB98AF4),
@ -22,9 +22,9 @@ fun SubscriptionScreen(
secondaryAction = PlaceholderAction("Settings", AppRoute.SETTINGS), secondaryAction = PlaceholderAction("Settings", AppRoute.SETTINGS),
chips = listOf("Entitlement", "Plan", "Restore"), chips = listOf("Entitlement", "Plan", "Restore"),
details = listOf( details = listOf(
"Entitlement display can stay plain-spoken", "See what your plan includes in plain language",
"Plan changes and restore purchase can stay together", "Keep restore purchase close to plan details",
"The upgrade path stays nearby" "Open the upgrade path when you are ready"
) )
) )
} }

View File

@ -76,7 +76,7 @@ class SpinWheelViewModel @Inject constructor(
} }
fun startSession() { fun startSession() {
_uiState.update { it.copy(navigateTo = AppRoute.wheelSession("local")) } _uiState.update { it.copy(navigateTo = AppRoute.wheelSession("session")) }
} }
fun onNavigated() { fun onNavigated() {

View File

@ -69,7 +69,7 @@ class WheelSessionViewModel @Inject constructor(
val state = _uiState.value val state = _uiState.value
sessionStore.lastAnswered = (state.currentIndex + 1).coerceAtMost(state.questions.size) sessionStore.lastAnswered = (state.currentIndex + 1).coerceAtMost(state.questions.size)
sessionStore.lastTotal = state.questions.size sessionStore.lastTotal = state.questions.size
_uiState.update { it.copy(navigateTo = AppRoute.wheelComplete("local")) } _uiState.update { it.copy(navigateTo = AppRoute.wheelComplete("session")) }
} }
fun onNavigated() { fun onNavigated() {

24
docs/copy-guide.md Normal file
View File

@ -0,0 +1,24 @@
# Copy Guide
## Voice
- Warm, calm, and direct.
- Speak to the couple, not to the implementation.
- Prefer concrete relationship language over product scaffolding.
- Keep labels short enough for small Android screens.
## Do
- Say "private answer", "saved reflection", "question pack", and "reveal".
- Use verbs for buttons: "Answer", "Reveal", "Open", "Remove".
- Let empty states offer a gentle next step.
- Name emotional intent when it helps: care, honesty, repair, closeness.
## Avoid
- Internal terms such as "local", "preview", "placeholder", "seeded", "database", and "later batch".
- Roadmap copy inside the app.
- Overexplaining technical state.
- Destructive language as a primary action unless the screen is explicitly confirming deletion.
## Examples
- Instead of "Loading local prompts": "Loading prompts".
- Instead of "No local answer yet": "No answer yet".
- Instead of "Partner sync can land here later": "Open a saved answer when you are ready to look at it together."