diff --git a/app/src/main/java/app/closer/core/navigation/AppNavigation.kt b/app/src/main/java/app/closer/core/navigation/AppNavigation.kt index c44593de..aab0d89b 100644 --- a/app/src/main/java/app/closer/core/navigation/AppNavigation.kt +++ b/app/src/main/java/app/closer/core/navigation/AppNavigation.kt @@ -202,7 +202,7 @@ fun AppNavigation( route = AppRoute.DAILY_QUESTION, deepLinks = listOf(navDeepLink { uriPattern = "closer://closer.app/daily_question" }) ) { - DailyQuestionScreen(onNavigate = navigateRoute) + DailyQuestionScreen(onNavigate = navigateRoute, onBack = navigateBackOrHome) } composable(route = AppRoute.QUESTION_PACKS) { QuestionPackLibraryScreen(onNavigate = navigateRoute) diff --git a/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt b/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt index 7e167a14..a01ef091 100644 --- a/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt +++ b/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt @@ -11,10 +11,17 @@ import app.closer.domain.model.Question @Composable fun DailyQuestionScreen( onNavigate: (String) -> Unit = {}, + onBack: () -> Unit = {}, viewModel: DailyQuestionViewModel = hiltViewModel() ) { val state by viewModel.uiState.collectAsState() + // Reveal is only offered once both partners have answered — the reveal screen enforces + // this too, but surfacing the button early is misleading and should be avoided. + val revealRoute: (() -> Unit)? = if (state.submitted && state.partnerHasAnswered) { + state.question?.let { q -> { onNavigate(AppRoute.answerReveal(q.id)) } } + } else null + LocalQuestionContent( state = state, title = "One question, enough space", @@ -25,20 +32,17 @@ fun DailyQuestionScreen( if (coupleId != null) { onNavigate(AppRoute.questionThread(coupleId, question.id)) } else { - // Discussing requires a paired partner; send unpaired users to invite one. onNavigate(AppRoute.CREATE_INVITE) } }, - onSecondaryRoute = state.question?.let { - { onNavigate(AppRoute.answerReveal(it.id)) } - }, - secondaryRouteLabel = "Reveal", + onSecondaryRoute = revealRoute, + secondaryRouteLabel = if (revealRoute != null) "Reveal" else null, onWrittenTextChanged = viewModel::updateWrittenText, onOptionToggled = viewModel::toggleOption, onScaleChanged = viewModel::updateScale, onSubmit = viewModel::submitAnswer, canSubmit = viewModel.canSubmit(), - onRefresh = viewModel::loadDailyQuestion + onBack = onBack ) } diff --git a/app/src/main/java/app/closer/ui/questions/DailyQuestionViewModel.kt b/app/src/main/java/app/closer/ui/questions/DailyQuestionViewModel.kt index 8436cf2e..a14fc0f7 100644 --- a/app/src/main/java/app/closer/ui/questions/DailyQuestionViewModel.kt +++ b/app/src/main/java/app/closer/ui/questions/DailyQuestionViewModel.kt @@ -28,7 +28,8 @@ data class LocalQuestionUiState( val submitted: Boolean = false, val pendingWrittenText: String = "", val pendingSelectedOptionIds: List = emptyList(), - val pendingScaleValue: Int = 3 + val pendingScaleValue: Int = 3, + val partnerHasAnswered: Boolean = false ) @HiltViewModel @@ -55,12 +56,16 @@ class DailyQuestionViewModel @Inject constructor( val today = FirestoreAnswerDataSource.todayLocalDateString() val (coupleId, question) = loadCoupleAndQuestion(today) val answer = question?.let { localAnswerRepository.getAnswer(it.id) } + val partnerHasAnswered = coupleId?.let { + runCatching { checkPartnerAnswered(it, today) }.getOrDefault(false) + } ?: false _uiState.value = LocalQuestionUiState( isLoading = false, question = question, coupleId = coupleId, dailyQuestionDate = today, - pendingScaleValue = defaultScaleValue(question) + pendingScaleValue = defaultScaleValue(question), + partnerHasAnswered = partnerHasAnswered ).withLocalAnswer(answer) } catch (e: Exception) { crashReporter.recordException(e) @@ -189,6 +194,17 @@ class DailyQuestionViewModel @Inject constructor( }.onFailure { crashReporter.recordException(it) } + // After submitting, refresh partner-answered status so the reveal button appears + // immediately if the partner answered while the user was composing. + val partnerHasAnswered = runCatching { checkPartnerAnswered(coupleId, dailyQuestionDate) }.getOrDefault(false) + _uiState.update { it.copy(partnerHasAnswered = partnerHasAnswered) } + } + + private suspend fun checkPartnerAnswered(coupleId: String, date: String): Boolean { + val userId = authRepository.currentUserId ?: return false + val couple = runCatching { coupleRepository.getCoupleForUser(userId) }.getOrNull() ?: return false + val partnerId = couple.userIds.firstOrNull { it != userId } ?: return false + return firestoreAnswerDataSource.getAnswerForUser(coupleId, partnerId, date) != null } } 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 36a60730..4d72b0d5 100644 --- a/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt +++ b/app/src/main/java/app/closer/ui/questions/LocalQuestionContent.kt @@ -23,11 +23,15 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape 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.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 import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Surface @@ -71,6 +75,7 @@ fun LocalQuestionContent( onSubmit: () -> Unit, canSubmit: Boolean, onRefresh: (() -> Unit)? = null, + onBack: (() -> Unit)? = null, modifier: Modifier = Modifier ) { val background = closerBackgroundBrush() @@ -89,6 +94,14 @@ fun LocalQuestionContent( .padding(horizontal = 20.dp, vertical = 18.dp), verticalArrangement = Arrangement.spacedBy(18.dp) ) { + if (onBack != null) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back" + ) + } + } LocalQuestionHeader(title = title, subtitle = subtitle) when {