fix(home): 'Play now' resumes the waiting game, not the generic hub (B-002 P2)
Resolve the active session's gameType to its resume route (gameRouteFor) and carry it on HomeAction.gameRoute / HomeUiState.waitingGameRoute; HomeActionTarget.Game now navigates there (fallback Play hub). Each game screen auto-joins the couple's active session on open, so the Home 'Play now' CTA drops the user straight into the actual waiting game. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e0d33ea85d
commit
c3c5438bcc
|
|
@ -216,7 +216,8 @@ private fun HomeCallbacks.toActionHandler(onNavigate: (String) -> Unit): (HomeAc
|
||||||
HomeActionTarget.QuestionPacks -> action.categoryId?.let(onCategory) ?: onPacks()
|
HomeActionTarget.QuestionPacks -> action.categoryId?.let(onCategory) ?: onPacks()
|
||||||
HomeActionTarget.Settings -> onSettings()
|
HomeActionTarget.Settings -> onSettings()
|
||||||
HomeActionTarget.AnswerReveal -> onReveal()
|
HomeActionTarget.AnswerReveal -> onReveal()
|
||||||
HomeActionTarget.Game -> onNavigate(AppRoute.PLAY)
|
// Resume the specific waiting game when known (B-002); fall back to the Play hub.
|
||||||
|
HomeActionTarget.Game -> onNavigate(action.gameRoute ?: AppRoute.PLAY)
|
||||||
HomeActionTarget.Challenge -> onNavigate(AppRoute.CONNECTION_CHALLENGES)
|
HomeActionTarget.Challenge -> onNavigate(AppRoute.CONNECTION_CHALLENGES)
|
||||||
HomeActionTarget.DatePlan -> onNavigate(AppRoute.DATE_MATCHES)
|
HomeActionTarget.DatePlan -> onNavigate(AppRoute.DATE_MATCHES)
|
||||||
HomeActionTarget.MemoryCapsule -> onNavigate(AppRoute.MEMORY_LANE)
|
HomeActionTarget.MemoryCapsule -> onNavigate(AppRoute.MEMORY_LANE)
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@ package app.closer.ui.home
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.closer.core.navigation.AppRoute
|
||||||
import app.closer.crypto.CoupleEncryptionManager
|
import app.closer.crypto.CoupleEncryptionManager
|
||||||
import app.closer.crypto.EncryptionStatus
|
import app.closer.crypto.EncryptionStatus
|
||||||
import app.closer.crypto.SealedRevealManager
|
import app.closer.crypto.SealedRevealManager
|
||||||
|
import app.closer.domain.model.GameType
|
||||||
import app.closer.data.remote.FirestoreAnswerDataSource
|
import app.closer.data.remote.FirestoreAnswerDataSource
|
||||||
import app.closer.data.remote.FirestoreCapsuleDataSource
|
import app.closer.data.remote.FirestoreCapsuleDataSource
|
||||||
import app.closer.data.remote.FirestoreChallengeDataSource
|
import app.closer.data.remote.FirestoreChallengeDataSource
|
||||||
|
|
@ -89,7 +91,10 @@ data class HomeAction(
|
||||||
val target: HomeActionTarget,
|
val target: HomeActionTarget,
|
||||||
val tone: HomeActionTone,
|
val tone: HomeActionTone,
|
||||||
val metric: String? = null,
|
val metric: String? = null,
|
||||||
val categoryId: String? = null
|
val categoryId: String? = null,
|
||||||
|
// For the "your partner is waiting to play" CTA: the specific game route to resume
|
||||||
|
// (so "Play now" jumps into the actual waiting game, not the generic Play hub). B-002.
|
||||||
|
val gameRoute: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PendingActionCard(
|
data class PendingActionCard(
|
||||||
|
|
@ -99,6 +104,19 @@ data class PendingActionCard(
|
||||||
val target: HomeActionTarget
|
val target: HomeActionTarget
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry route that resumes an in-progress game of [gameType]. Each game screen
|
||||||
|
* detects the couple's active session on open and joins it, so navigating here lets the
|
||||||
|
* Home "Play now" CTA drop the user straight back into the waiting game (B-002).
|
||||||
|
*/
|
||||||
|
private fun gameRouteFor(gameType: String?): String? = when (gameType) {
|
||||||
|
GameType.WHEEL -> AppRoute.SPIN_WHEEL_RANDOM
|
||||||
|
GameType.THIS_OR_THAT -> AppRoute.THIS_OR_THAT
|
||||||
|
GameType.HOW_WELL -> AppRoute.HOW_WELL
|
||||||
|
GameType.DESIRE_SYNC -> AppRoute.DESIRE_SYNC
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
enum class DailyQuestionState {
|
enum class DailyQuestionState {
|
||||||
UNANSWERED,
|
UNANSWERED,
|
||||||
USER_ANSWERED_PARTNER_PENDING,
|
USER_ANSWERED_PARTNER_PENDING,
|
||||||
|
|
@ -128,6 +146,9 @@ data class HomeUiState(
|
||||||
val pendingActions: List<PendingActionCard> = emptyList(),
|
val pendingActions: List<PendingActionCard> = emptyList(),
|
||||||
// Retention signals — populated in loadHome() and observeAnswers()
|
// Retention signals — populated in loadHome() and observeAnswers()
|
||||||
val hasWaitingGame: Boolean = false,
|
val hasWaitingGame: Boolean = false,
|
||||||
|
// The route of the active game waiting for this user, so the Home "Play now" CTA
|
||||||
|
// resumes that specific game instead of dumping on the generic Play hub (B-002).
|
||||||
|
val waitingGameRoute: String? = null,
|
||||||
val hasActiveChallenge: Boolean = false,
|
val hasActiveChallenge: Boolean = false,
|
||||||
val hasUpcomingDatePlan: Boolean = false,
|
val hasUpcomingDatePlan: Boolean = false,
|
||||||
val hasUnlockedCapsule: Boolean = false,
|
val hasUnlockedCapsule: Boolean = false,
|
||||||
|
|
@ -243,6 +264,7 @@ class HomeViewModel @Inject constructor(
|
||||||
|
|
||||||
// Retention signal fetches — run in parallel, failures silently default to false.
|
// Retention signal fetches — run in parallel, failures silently default to false.
|
||||||
var hasWaitingGame = false
|
var hasWaitingGame = false
|
||||||
|
var waitingGameRoute: String? = null
|
||||||
var hasActiveChallenge = false
|
var hasActiveChallenge = false
|
||||||
var hasUpcomingDatePlan = false
|
var hasUpcomingDatePlan = false
|
||||||
var hasUnlockedCapsule = false
|
var hasUnlockedCapsule = false
|
||||||
|
|
@ -252,8 +274,9 @@ class HomeViewModel @Inject constructor(
|
||||||
val gameJob = async {
|
val gameJob = async {
|
||||||
runCatching {
|
runCatching {
|
||||||
val session = questionSessionRepository.getActiveSessionForCouple(coupleId)
|
val session = questionSessionRepository.getActiveSessionForCouple(coupleId)
|
||||||
session != null && uid !in session.completedByUsers
|
?.takeIf { uid !in it.completedByUsers }
|
||||||
}.getOrDefault(false)
|
session to gameRouteFor(session?.gameType)
|
||||||
|
}.getOrDefault(null to null)
|
||||||
}
|
}
|
||||||
val challengeJob = async {
|
val challengeJob = async {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|
@ -276,7 +299,9 @@ class HomeViewModel @Inject constructor(
|
||||||
.any { it.status == "sealed" && it.unlockAt in 1L..now }
|
.any { it.status == "sealed" && it.unlockAt in 1L..now }
|
||||||
}.getOrDefault(false)
|
}.getOrDefault(false)
|
||||||
}
|
}
|
||||||
hasWaitingGame = gameJob.await()
|
val (waitingSession, waitingRoute) = gameJob.await()
|
||||||
|
hasWaitingGame = waitingSession != null
|
||||||
|
waitingGameRoute = waitingRoute
|
||||||
hasActiveChallenge = challengeJob.await()
|
hasActiveChallenge = challengeJob.await()
|
||||||
hasUpcomingDatePlan = dateJob.await()
|
hasUpcomingDatePlan = dateJob.await()
|
||||||
hasUnlockedCapsule = capsuleJob.await()
|
hasUnlockedCapsule = capsuleJob.await()
|
||||||
|
|
@ -295,6 +320,7 @@ class HomeViewModel @Inject constructor(
|
||||||
partnerLeftEvent = false,
|
partnerLeftEvent = false,
|
||||||
needsRecovery = needsRecovery,
|
needsRecovery = needsRecovery,
|
||||||
hasWaitingGame = hasWaitingGame,
|
hasWaitingGame = hasWaitingGame,
|
||||||
|
waitingGameRoute = waitingGameRoute,
|
||||||
hasActiveChallenge = hasActiveChallenge,
|
hasActiveChallenge = hasActiveChallenge,
|
||||||
hasUpcomingDatePlan = hasUpcomingDatePlan,
|
hasUpcomingDatePlan = hasUpcomingDatePlan,
|
||||||
hasUnlockedCapsule = hasUnlockedCapsule,
|
hasUnlockedCapsule = hasUnlockedCapsule,
|
||||||
|
|
@ -598,7 +624,8 @@ class HomeViewModel @Inject constructor(
|
||||||
body = "A game is ready for the two of you. Jump back in and keep the ritual going.",
|
body = "A game is ready for the two of you. Jump back in and keep the ritual going.",
|
||||||
cta = "Play now",
|
cta = "Play now",
|
||||||
target = HomeActionTarget.Game,
|
target = HomeActionTarget.Game,
|
||||||
tone = HomeActionTone.Ritual
|
tone = HomeActionTone.Ritual,
|
||||||
|
gameRoute = waitingGameRoute
|
||||||
)
|
)
|
||||||
|
|
||||||
Priority.CHALLENGE_WAITING -> HomeAction(
|
Priority.CHALLENGE_WAITING -> HomeAction(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue