diff --git a/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt b/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt index 8436607f..dc1cf77d 100644 --- a/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt +++ b/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt @@ -6,6 +6,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.slideInVertically +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -27,8 +28,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -37,20 +36,17 @@ import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -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.platform.LocalContext import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import app.closer.R import app.closer.domain.model.ChoiceAnswerConfigImpl import app.closer.domain.model.Question import app.closer.domain.model.ThisOrThatAnswerConfigImpl @@ -121,12 +117,11 @@ fun LocalQuestionContent( val reducedMotion = Settings.Global.getFloat( context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f ) == 0f - var helpExpanded by remember(question.id) { mutableStateOf(false) } - QuestionMetaRow(question = question) QuestionHeader( question = question, - helpExpanded = helpExpanded, - onToggleHelp = { helpExpanded = !helpExpanded }, + helpExpanded = false, + onToggleHelp = {}, + showHelp = false ) QuestionAnswerInput( question = question, @@ -205,50 +200,47 @@ private fun LocalQuestionHeader( title: String, subtitle: String ) { - Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { - Text( - text = title, - style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.SemiBold), - color = MaterialTheme.colorScheme.onSurface - ) - Text( - text = subtitle, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - maxLines = 3, - overflow = TextOverflow.Ellipsis - ) - } -} - - -@Composable -private fun QuestionMetaRow(question: Question) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - MetaPill(label = question.category.displayCategoryName()) - MetaPill(label = "Depth ${question.depthLevel}") - MetaPill(label = question.type.displayQuestionType()) - } -} - -@Composable -private fun MetaPill(label: String) { Surface( - shape = RoundedCornerShape(999.dp), - color = Color.White.copy(alpha = 0.72f), - shadowElevation = 0.dp + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(28.dp), + color = Color.White.copy(alpha = 0.82f), + shadowElevation = 5.dp ) { - Text( - text = label, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { + Image( + painter = painterResource(R.drawable.illustration_daily_question), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxWidth() + .height(166.dp) + .clip(RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp)) + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(start = 20.dp, end = 20.dp, bottom = 20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold), + color = MaterialTheme.colorScheme.onSurface, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + Text( + text = subtitle, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 3, + overflow = TextOverflow.Ellipsis + ) + } + } } } diff --git a/app/src/main/java/app/closer/ui/questions/components/QuestionHeader.kt b/app/src/main/java/app/closer/ui/questions/components/QuestionHeader.kt index c6b42ddc..dc9e5ee2 100644 --- a/app/src/main/java/app/closer/ui/questions/components/QuestionHeader.kt +++ b/app/src/main/java/app/closer/ui/questions/components/QuestionHeader.kt @@ -10,6 +10,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -22,21 +23,22 @@ fun QuestionHeader( question: Question, helpExpanded: Boolean, onToggleHelp: () -> Unit, + showHelp: Boolean = true, modifier: Modifier = Modifier ) { Column(modifier = modifier.fillMaxWidth()) { Card( modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(20.dp), + shape = RoundedCornerShape(26.dp), colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant + containerColor = Color(0xFFFFF8FC).copy(alpha = 0.94f) ), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + elevation = CardDefaults.cardElevation(defaultElevation = 5.dp) ) { Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 24.dp, vertical = 28.dp) + .padding(horizontal = 24.dp, vertical = 30.dp) ) { Text( text = question.text, @@ -45,19 +47,21 @@ fun QuestionHeader( fontWeight = FontWeight.SemiBold, lineHeight = 34.sp ), - color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Start, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center, maxLines = 6, overflow = TextOverflow.Ellipsis ) } } - QuestionHelpExpandable( - question = question, - expanded = helpExpanded, - onToggle = onToggleHelp, - modifier = Modifier.padding(top = 8.dp) - ) + if (showHelp) { + QuestionHelpExpandable( + question = question, + expanded = helpExpanded, + onToggle = onToggleHelp, + modifier = Modifier.padding(top = 8.dp) + ) + } } } diff --git a/app/src/main/res/drawable-nodpi/illustration_daily_question.png b/app/src/main/res/drawable-nodpi/illustration_daily_question.png new file mode 100644 index 00000000..71812935 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/illustration_daily_question.png differ diff --git a/iphone/Closer/Questions/QuestionViews.swift b/iphone/Closer/Questions/QuestionViews.swift index 53f1768e..1d078800 100644 --- a/iphone/Closer/Questions/QuestionViews.swift +++ b/iphone/Closer/Questions/QuestionViews.swift @@ -11,14 +11,16 @@ struct DailyQuestionView: View { var body: some View { ScrollView { - VStack(spacing: CloserSpacing.xxl) { + VStack(spacing: CloserSpacing.lg) { if isLoading { LoadingView(message: "Loading today's question...") .padding(.top, CloserSpacing.xxxl) } else if let question = question { - // Question card + TodayQuestionHeroView() + .closerPadding() + VStack(spacing: CloserSpacing.lg) { - Text("Today's Question") + Text("Today's question") .font(CloserFont.subheadline) .foregroundColor(.closerTextSecondary) @@ -143,6 +145,35 @@ struct DailyQuestionView: View { } } +private struct TodayQuestionHeroView: View { + var body: some View { + VStack(alignment: .leading, spacing: CloserSpacing.md) { + Image("illustration-daily-question") + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity) + .frame(height: 174) + .clipShape(RoundedRectangle(cornerRadius: CloserRadius.xlarge, style: .continuous)) + .accessibilityHidden(true) + + VStack(alignment: .leading, spacing: CloserSpacing.xs) { + Text("One question, enough space") + .font(CloserFont.title2) + .foregroundColor(.closerText) + + Text("Answer privately first, then reveal when you are both ready.") + .font(CloserFont.callout) + .foregroundColor(.closerTextSecondary) + .fixedSize(horizontal: false, vertical: true) + } + } + .padding(CloserSpacing.md) + .background(Color.closerSurface) + .clipShape(RoundedRectangle(cornerRadius: CloserRadius.xlarge, style: .continuous)) + .closerShadow(level: .small) + } +} + // MARK: - Question Answer struct QuestionAnswerView: View { diff --git a/iphone/Closer/Resources/illustration-daily-question.png b/iphone/Closer/Resources/illustration-daily-question.png new file mode 100644 index 00000000..71812935 Binary files /dev/null and b/iphone/Closer/Resources/illustration-daily-question.png differ diff --git a/iphone/Closer/Wheel/WheelViews.swift b/iphone/Closer/Wheel/WheelViews.swift index 16990195..adac7e3a 100644 --- a/iphone/Closer/Wheel/WheelViews.swift +++ b/iphone/Closer/Wheel/WheelViews.swift @@ -58,6 +58,35 @@ struct CategoryPickerView: View { } } +// MARK: - Wheel Copy + +private enum WheelCopy { + static let titles = [ + "Let the Prompt Find You", + "Spin for Tonight's Prompt", + "Let Fate Pick", + "The Wheel Has Opinions", + "Find Our Prompt", + "Tiny Moment, Big Maybe", + ] + static let subtitles = [ + "Spin for tonight's prompt.", + "Let fate pick the vibe.", + "Not sure? Spin.", + "Tiny moment. Big maybe.", + "No pressure. Just spin.", + "Let the wheel choose.", + ] + static let buttonLabels = ["Spin", "Let's Spin"] + static let results = [ + "The wheel has spoken.", + "This one found you.", + "Fate picked this one.", + "Prompt unlocked.", + "This is the one.", + ] +} + // MARK: - Spin Wheel struct SpinWheelView: View { @@ -67,6 +96,10 @@ struct SpinWheelView: View { @State private var selectedSlice: Int? @State private var showResult = false @State private var navigateToSession = false + @State private var titleIndex: Int = 0 + @State private var subtitleIndex: Int = 0 + @State private var buttonIndex: Int = 0 + @State private var resultIndex: Int = 0 let slices: [(label: String, color: Color)] = [ ("Question", .closerPrimary), @@ -81,9 +114,23 @@ struct SpinWheelView: View { var body: some View { VStack(spacing: CloserSpacing.xl) { - Text("Category: \(category)") - .font(CloserFont.title2) - .foregroundColor(.closerText) + VStack(spacing: CloserSpacing.xs) { + Text(WheelCopy.titles[titleIndex]) + .font(CloserFont.title2) + .foregroundColor(.closerText) + .multilineTextAlignment(.center) + Text(WheelCopy.subtitles[subtitleIndex]) + .font(CloserFont.subheadline) + .foregroundColor(.closerTextSecondary) + Text(category) + .font(CloserFont.caption) + .foregroundColor(.closerPrimary) + .padding(.horizontal, CloserSpacing.sm) + .padding(.vertical, 4) + .background(Color.closerPrimary.opacity(0.1)) + .clipShape(Capsule()) + } + .padding(.top, CloserSpacing.sm) // Wheel ZStack { @@ -114,8 +161,8 @@ struct SpinWheelView: View { // Result if let slice = selectedSlice, showResult { - VStack(spacing: CloserSpacing.sm) { - Text("You got:") + VStack(spacing: CloserSpacing.xs) { + Text(WheelCopy.results[resultIndex]) .font(CloserFont.subheadline) .foregroundColor(.closerTextSecondary) Text(slices[slice].label) @@ -132,7 +179,7 @@ struct SpinWheelView: View { Button(action: spin) { HStack { Image(systemName: "arrow.triangle.2.circlepath") - Text(isSpinning ? "Spinning..." : "Spin!") + Text(isSpinning ? "Spinning…" : WheelCopy.buttonLabels[buttonIndex]) } } .buttonStyle(PrimaryButtonStyle(isDisabled: isSpinning)) @@ -152,6 +199,11 @@ struct SpinWheelView: View { .navigationDestination(isPresented: $navigateToSession) { WheelSessionView(sessionId: UUID().uuidString, category: category, slice: slices[selectedSlice ?? 0].label) } + .onAppear { + titleIndex = Int.random(in: 0..